+++ /dev/null
-
-=============
-0.1 Changelog
-=============
-
-
-.. changelog::
- :version: 0.1.7
- :released: Fri May 05 2006
-
- .. change::
- :tags:
- :tickets:
-
- some fixes to topological sort algorithm
-
- .. change::
- :tags:
- :tickets:
-
- added DISTINCT ON support to Postgres (just supply distinct=[col1,col2..])
-
- .. change::
- :tags:
- :tickets:
-
- added __mod__ (% operator) to sql expressions
-
- .. change::
- :tags:
- :tickets:
-
- "order_by" mapper property inherited from inheriting mapper
-
- .. change::
- :tags:
- :tickets:
-
- fix to column type used when mapper UPDATES/DELETEs
-
- .. change::
- :tags:
- :tickets:
-
- with convert_unicode=True, reflection was failing, has been fixed
-
- .. change::
- :tags:
- :tickets:
-
- types types types! still weren't working....have to use TypeDecorator again :(
-
- .. change::
- :tags:
- :tickets:
-
- mysql binary type converts array output to buffer, fixes PickleType
-
- .. change::
- :tags:
- :tickets:
-
- fixed the attributes.py memory leak once and for all
-
- .. change::
- :tags:
- :tickets:
-
- unittests are qualified based on the databases that support each one
-
- .. change::
- :tags:
- :tickets:
-
- fixed bug where column defaults would clobber VALUES clause of insert objects
-
- .. change::
- :tags:
- :tickets:
-
- fixed bug where table def w/ schema name would force engine connection
-
- .. change::
- :tags:
- :tickets:
-
- fix for parenthesis to work correctly with subqueries in INSERT/UPDATE
-
- .. change::
- :tags:
- :tickets:
-
- HistoryArraySet gets extend() method
-
- .. change::
- :tags:
- :tickets:
-
- fixed lazyload support for other comparison operators besides =
-
- .. change::
- :tags:
- :tickets:
-
- lazyload fix where two comparisons in the join condition point to the
- samem column
-
- .. change::
- :tags:
- :tickets:
-
- added "construct_new" flag to mapper, will use __new__ to create instances
- instead of __init__ (standard in 0.2)
-
- .. change::
- :tags:
- :tickets:
-
- added selectresults.py to SVN, missed it last time
-
- .. change::
- :tags:
- :tickets:
-
- tweak to allow a many-to-many relationship from a table to itself via
- an association table
-
- .. change::
- :tags:
- :tickets:
-
- small fix to "translate_row" function used by polymorphic example
-
- .. change::
- :tags:
- :tickets:
-
- create_engine uses cgi.parse_qsl to read query string (out the window in 0.2)
-
- .. change::
- :tags:
- :tickets:
-
- tweaks to CAST operator
-
- .. change::
- :tags:
- :tickets:
-
- fixed function names LOCAL_TIME/LOCAL_TIMESTAMP -> LOCALTIME/LOCALTIMESTAMP
-
- .. change::
- :tags:
- :tickets:
-
- fixed order of ORDER BY/HAVING in compile
-
-.. changelog::
- :version: 0.1.6
- :released: Wed Apr 12 2006
-
- .. change::
- :tags:
- :tickets:
-
- support for MS-SQL added courtesy Rick Morrison, Runar Petursson
-
- .. change::
- :tags:
- :tickets:
-
- the latest SQLSoup from J. Ellis
-
- .. change::
- :tags:
- :tickets:
-
- ActiveMapper has preliminary support for inheritance (Jeff Watkins)
-
- .. change::
- :tags:
- :tickets:
-
- added a "mods" system which allows pluggable modules that modify/augment
- core functionality, using the function "install_mods(\*modnames)".
-
- .. change::
- :tags:
- :tickets:
-
- added the first "mod", SelectResults, which modifies mapper selects to
- return generators that turn ranges into LIMIT/OFFSET queries
- (Jonas Borgstr?
-
- .. change::
- :tags:
- :tickets:
-
- factored out querying capabilities of Mapper into a separate Query object
- which is Session-centric. this improves the performance of mapper.using(session)
- and makes other things possible.
-
- .. change::
- :tags:
- :tickets:
-
- objectstore/Session refactored, the official way to save objects is now
- via the flush() method. The begin/commit functionality of Session is factored
- into LegacySession which is still established as the default behavior, until
- the 0.2 series.
-
- .. change::
- :tags:
- :tickets:
-
- types system is bound to an engine at query compile time, not schema
- construction time. this simplifies the types system as well as the ProxyEngine.
-
- .. change::
- :tags:
- :tickets:
-
- added 'version_id' keyword argument to mapper. this keyword should reference a
- Column object with type Integer, preferably non-nullable, which will be used on
- the mapped table to track version numbers. this number is incremented on each
- save operation and is specified in the UPDATE/DELETE conditions so that it
- factors into the returned row count, which results in a ConcurrencyError if the
- value received is not the expected count.
-
- .. change::
- :tags:
- :tickets:
-
- added 'entity_name' keyword argument to mapper. a mapper is now associated
- with a class via the class object as well as an optional entity_name parameter,
- which is a string defaulting to None. any number of primary mappers can be
- created for a class, qualified by the entity name. instances of those classes
- will issue all of their load and save operations through their
- entity_name-qualified mapper, and maintain separate a identity in the identity
- map for an otherwise equivalent object.
-
- .. change::
- :tags:
- :tickets:
-
- overhaul to the attributes system. code has been clarified, and also fixed to
- support proper polymorphic behavior on object attributes.
-
- .. change::
- :tags:
- :tickets:
-
- added "for_update" flag to Select objects
-
- .. change::
- :tags:
- :tickets:
-
- some fixes for backrefs
-
- .. change::
- :tags:
- :tickets:
-
- fix for postgres1 DateTime type
-
- .. change::
- :tags:
- :tickets:
-
- documentation pages mostly switched over to Markdown syntax
-
-.. changelog::
- :version: 0.1.5
- :released: Mon Mar 27 2006
-
- .. change::
- :tags:
- :tickets:
-
- added SQLSession concept to SQLEngine. this object keeps track of retrieving a
- connection from the connection pool as well as an in-progress transaction.
- methods push_session() and pop_session() added to SQLEngine which push/pop a new
- SQLSession onto the engine, allowing operation upon a second connection "nested"
- within the previous one, allowing nested transactions. Other tricks are sure to
- come later regarding SQLSession.
-
- .. change::
- :tags:
- :tickets:
-
- added nest_on argument to objectstore.Session. This is a single SQLEngine or
- list of engines for which push_session()/pop_session() will be called each time
- this Session becomes the active session (via objectstore.push_session() or
- equivalent). This allows a unit of work Session to take advantage of the nested
- transaction feature without explicitly calling push_session/pop_session on the
- engine.
-
- .. change::
- :tags:
- :tickets:
-
- factored apart objectstore/unitofwork to separate "Session scoping" from
- "uow commit heavy lifting"
-
- .. change::
- :tags:
- :tickets:
-
- added populate_instance() method to MapperExtension. allows an extension to
- modify the population of object attributes. this method can call the
- populate_instance() method on another mapper to proxy the attribute population
- from one mapper to another; some row translation logic is also built in to help
- with this.
-
- .. change::
- :tags:
- :tickets:
-
- fixed Oracle8-compatibility "use_ansi" flag which converts JOINs to
- comparisons with the = and (+) operators, passes basic unittests
-
- .. change::
- :tags:
- :tickets:
-
- tweaks to Oracle LIMIT/OFFSET support
-
- .. change::
- :tags:
- :tickets:
-
- Oracle reflection uses ALL_** views instead of USER_** to get larger
- list of stuff to reflect from
-
- .. change::
- :tags:
- :tickets: 105
-
- fixes to Oracle foreign key reflection
-
- .. change::
- :tags:
- :tickets:
-
- objectstore.commit(obj1, obj2,...) adds an extra step to seek out private
- relations on properties and delete child objects, even though its not a global
- commit
-
- .. change::
- :tags:
- :tickets:
-
- lots and lots of fixes to mappers which use inheritance, strengthened the
- concept of relations on a mapper being made towards the "local" table for that
- mapper, not the tables it inherits. allows more complex compositional patterns
- to work with lazy/eager loading.
-
- .. change::
- :tags:
- :tickets:
-
- added support for mappers to inherit from others based on the same table,
- just specify the same table as that of both parent/child mapper.
-
- .. change::
- :tags:
- :tickets:
-
- some minor speed improvements to the attributes system with regards to
- instantiating and populating new objects.
-
- .. change::
- :tags:
- :tickets:
-
- fixed MySQL binary unit test
-
- .. change::
- :tags:
- :tickets:
-
- INSERTs can receive clause elements as VALUES arguments, not just literal
- values
-
- .. change::
- :tags:
- :tickets:
-
- support for calling multi-tokened functions, i.e. schema.mypkg.func()
-
- .. change::
- :tags:
- :tickets:
-
- added J. Ellis' SQLSoup module to extensions package
-
- .. change::
- :tags:
- :tickets:
-
- added "polymorphic" examples illustrating methods to load multiple object types
- from one mapper, the second of which uses the new populate_instance() method.
- small improvements to mapper, UNION construct to help the examples along
-
- .. change::
- :tags:
- :tickets:
-
- improvements/fixes to session.refresh()/session.expire() (which may have
- been called "invalidate" earlier..)
-
- .. change::
- :tags:
- :tickets:
-
- added session.expunge() which totally removes an object from the current
- session
-
- .. change::
- :tags:
- :tickets:
-
- added \*args, \**kwargs pass-through to engine.transaction(func) allowing easier
- creation of transactionalizing decorator functions
-
- .. change::
- :tags:
- :tickets:
-
- added iterator interface to ResultProxy: "for row in result:..."
-
- .. change::
- :tags:
- :tickets:
-
- added assertion to tx = session.begin(); tx.rollback(); tx.begin(), i.e. can't
- use it after a rollback()
-
- .. change::
- :tags:
- :tickets:
-
- added date conversion on bind parameter fix to SQLite enabling dates to
- work with pysqlite1
-
- .. change::
- :tags:
- :tickets: 116
-
- improvements to subqueries to more intelligently construct their FROM
- clauses
-
- .. change::
- :tags:
- :tickets:
-
- added PickleType to types.
-
- .. change::
- :tags:
- :tickets:
-
- fixed two bugs with column labels with regards to bind parameters: bind param
- keynames they are now generated from a column "label" in all relevant cases to
- take advantage of excess-name-length rules, and checks for a peculiar collision
- against a column named the same as "tablename_colname" added
-
- .. change::
- :tags:
- :tickets:
-
- major overhaul to unit of work documentation, other documentation sections.
-
- .. change::
- :tags:
- :tickets:
-
- fixed attributes bug where if an object is committed, its lazy-loaded list got
- blown away if it hadn't been loaded
-
- .. change::
- :tags:
- :tickets:
-
- added unique_connection() method to engine, connection pool to return a
- connection that is not part of the thread-local context or any current
- transaction
-
- .. change::
- :tags:
- :tickets:
-
- added invalidate() function to pooled connection. will remove the connection
- from the pool. still need work for engines to auto-reconnect to a stale DB
- though.
-
- .. change::
- :tags:
- :tickets:
-
- added distinct() function to column elements so you can do
- func.count(mycol.distinct())
-
- .. change::
- :tags:
- :tickets:
-
- added "always_refresh" flag to Mapper, creates a mapper that will always
- refresh the attributes of objects it gets/selects from the DB, overwriting any
- changes made.
-
-.. changelog::
- :version: 0.1.4
- :released: Mon Mar 13 2006
-
- .. change::
- :tags:
- :tickets:
-
- create_engine() now uses genericized parameters; host/hostname,
- db/dbname/database, password/passwd, etc. for all engine connections. makes
- engine URIs much more "universal"
-
- .. change::
- :tags:
- :tickets:
-
- added support for SELECT statements embedded into a column clause, using the
- flag "scalar=True"
-
- .. change::
- :tags:
- :tickets:
-
- another overhaul to EagerLoading when used in conjunction with mappers that
- inherit; improvements to eager loads figuring out their aliased queries
- correctly, also relations set up against a mapper with inherited mappers will
- create joins against the table that is specific to the mapper itself (i.e. and
- not any tables that are inherited/are further down the inheritance chain),
- this can be overridden by using custom primary/secondary joins.
-
- .. change::
- :tags:
- :tickets:
-
- added J.Ellis patch to mapper.py so that selectone() throws an exception
- if query returns more than one object row, selectfirst() to not throw the
- exception. also adds selectfirst_by (synonymous with get_by) and selectone_by
-
- .. change::
- :tags:
- :tickets:
-
- added onupdate parameter to Column, will exec SQL/python upon an update
- statement.Also adds "for_update=True" to all DefaultGenerator subclasses
-
- .. change::
- :tags:
- :tickets:
-
- added support for Oracle table reflection contributed by Andrija Zaric;
- still some bugs to work out regarding composite primary keys/dictionary selection
-
- .. change::
- :tags:
- :tickets:
-
- checked in an initial Firebird module, awaiting testing.
-
- .. change::
- :tags:
- :tickets:
-
- added sql.ClauseParameters dictionary object as the result for
- compiled.get_params(), does late-typeprocessing of bind parameters so
- that the original values are easier to access
-
- .. change::
- :tags:
- :tickets:
-
- more docs for indexes, column defaults, connection pooling, engine construction
-
- .. change::
- :tags:
- :tickets:
-
- overhaul to the construction of the types system. uses a simpler inheritance
- pattern so that any of the generic types can be easily subclassed, with no need
- for TypeDecorator.
-
- .. change::
- :tags:
- :tickets:
-
- added "convert_unicode=False" parameter to SQLEngine, will cause all String
- types to perform unicode encoding/decoding (makes Strings act like Unicodes)
-
- .. change::
- :tags:
- :tickets:
-
- added 'encoding="utf8"' parameter to engine. the given encoding will be
- used for all encode/decode calls within Unicode types as well as Strings
- when convert_unicode=True.
-
- .. change::
- :tags:
- :tickets:
-
- improved support for mapping against UNIONs, added polymorph.py example
- to illustrate multi-class mapping against a UNION
-
- .. change::
- :tags:
- :tickets:
-
- fix to SQLite LIMIT/OFFSET syntax
-
- .. change::
- :tags:
- :tickets:
-
- fix to Oracle LIMIT syntax
-
- .. change::
- :tags:
- :tickets:
-
- added backref() function, allows backreferences to have keyword arguments
- that will be passed to the backref.
-
- .. change::
- :tags:
- :tickets:
-
- Sequences and ColumnDefault objects can do execute()/scalar() standalone
-
- .. change::
- :tags:
- :tickets:
-
- SQL functions (i.e. func.foo()) can do execute()/scalar() standalone
-
- .. change::
- :tags:
- :tickets:
-
- fix to SQL functions so that the ANSI-standard functions, i.e. current_timestamp
- etc., do not specify parenthesis. all other functions do.
-
- .. change::
- :tags:
- :tickets:
-
- added settattr_clean and append_clean to SmartProperty, which set
- attributes without triggering a "dirty" event or any history. used as:
- myclass.prop1.setattr_clean(myobject, 'hi')
-
- .. change::
- :tags:
- :tickets:
-
- improved support to column defaults when used by mappers; mappers will pull
- pre-executed defaults from statement's executed bind parameters
- (pre-conversion) to populate them into a saved object's attributes; if any
- PassiveDefaults have fired off, will instead post-fetch the row from the DB to
- populate the object.
-
- .. change::
- :tags:
- :tickets:
-
- added 'get_session().invalidate(\*obj)' method to objectstore, instances will
- refresh() themselves upon the next attribute access.
-
- .. change::
- :tags:
- :tickets:
-
- improvements to SQL func calls including an "engine" keyword argument so
- they can be execute()d or scalar()ed standalone, also added func accessor to
- SQLEngine
-
- .. change::
- :tags:
- :tickets:
-
- fix to MySQL4 custom table engines, i.e. TYPE instead of ENGINE
-
- .. change::
- :tags:
- :tickets:
-
- slightly enhanced logging, includes timestamps and a somewhat configurable
- formatting system, in lieu of a full-blown logging system
-
- .. change::
- :tags:
- :tickets:
-
- improvements to the ActiveMapper class from the TG gang, including
- many-to-many relationships
-
- .. change::
- :tags:
- :tickets:
-
- added Double and TinyInt support to mysql
-
-.. changelog::
- :version: 0.1.3
- :released: Thu Mar 02 2006
-
- .. change::
- :tags:
- :tickets:
-
- completed "post_update" feature, will add a second update statement before
- inserts and after deletes in order to reconcile a relationship without any
- dependencies being created; used when persisting two rows that are dependent
- on each other
-
- .. change::
- :tags:
- :tickets:
-
- completed mapper.using(session) function, localized per-object Session
- functionality; objects can be declared and manipulated as local to any
- user-defined Session
-
- .. change::
- :tags:
- :tickets:
-
- fix to Oracle "row_number over" clause with multiple tables
-
- .. change::
- :tags:
- :tickets:
-
- mapper.get() was not selecting multiple-keyed objects if the mapper's table was a join,
- such as in an inheritance relationship, this is fixed.
-
- .. change::
- :tags:
- :tickets:
-
- overhaul to sql/schema packages so that the sql package can run all on its own,
- producing selects, inserts, etc. without any engine dependencies. builds upon
- new TableClause/ColumnClause lexical objects. Schema's Table/Column objects
- are the "physical" subclasses of them. simplifies schema/sql relationship,
- extensions (like proxyengine), and speeds overall performance by a large margin.
- removes the entire getattr() behavior that plagued 0.1.1.
-
- .. change::
- :tags:
- :tickets:
-
- refactoring of how the mapper "synchronizes" data between two objects into a
- separate module, works better with properties attached to a mapper that has an
- additional inheritance relationship to one of the related tables, also the same
- methodology used to synchronize parent/child objects now used by mapper to
- synchronize between inherited and inheriting mappers.
-
- .. change::
- :tags:
- :tickets:
-
- made objectstore "check for out-of-identitymap" more aggressive, will perform the
- check when object attributes are modified or the object is deleted
-
- .. change::
- :tags:
- :tickets:
-
- Index object fully implemented, can be constructed standalone, or via
- "index" and "unique" arguments on Columns.
-
- .. change::
- :tags:
- :tickets:
-
- added "convert_unicode" flag to SQLEngine, will treat all String/CHAR types
- as Unicode types, with raw-byte/utf-8 translation on the bind parameter and
- result set side.
-
- .. change::
- :tags:
- :tickets:
-
- postgres maintains a list of ANSI functions that must have no parenthesis so
- function calls with no arguments work consistently
-
- .. change::
- :tags:
- :tickets:
-
- tables can be created with no engine specified. this will default their engine
- to a module-scoped "default engine" which is a ProxyEngine. this engine can
- be connected via the function "global_connect".
-
- .. change::
- :tags:
- :tickets:
-
- added "refresh(\*obj)" method to objectstore / Session to reload the attributes of
- any set of objects from the database unconditionally
-
-.. changelog::
- :version: 0.1.2
- :released: Fri Feb 24 2006
-
- .. change::
- :tags:
- :tickets:
-
- fixed a recursive call in schema that was somehow running 994 times then returning
- normally. broke nothing, slowed down everything. thanks to jpellerin for finding this.
-
-.. changelog::
- :version: 0.1.1
- :released: Thu Feb 23 2006
-
- .. change::
- :tags:
- :tickets:
-
- small fix to Function class so that expressions with a func.foo() use the type of the
- Function object (i.e. the left side) as the type of the boolean expression, not the
- other side which is more of a moving target (changeset 1020).
-
- .. change::
- :tags:
- :tickets:
-
- creating self-referring mappers with backrefs slightly easier (but still not that easy -
- changeset 1019)
-
- .. change::
- :tags:
- :tickets:
-
- fixes to one-to-one mappings (changeset 1015)
-
- .. change::
- :tags:
- :tickets:
-
- psycopg1 date/time issue with None fixed (changeset 1005)
-
- .. change::
- :tags:
- :tickets:
-
- two issues related to postgres, which doesn't want to give you the "lastrowid"
- since oids are deprecated:
-
- * postgres database-side defaults that are on primary key cols *do* execute
- explicitly beforehand, even though that's not the idea of a PassiveDefault. this is
- because sequences on columns get reflected as PassiveDefaults, but need to be explicitly
- executed on a primary key col so we know what we just inserted.
- * if you did add a row that has a bunch of database-side defaults on it,
- and the PassiveDefault thing was working the old way, i.e. they just execute on
- the DB side, the "can't get the row back without an OID" exception that occurred
- also will not happen unless someone (usually the ORM) explicitly asks for it.
-
- .. change::
- :tags:
- :tickets:
-
- fixed a glitch with engine.execute_compiled where it was making a second
- ResultProxy that just got thrown away.
-
- .. change::
- :tags:
- :tickets:
-
- began to implement newer logic in object properties. you can now say
- myclass.attr.property, which will give you the PropertyLoader corresponding to that
- attribute, i.e. myclass.mapper.props['attr']
-
- .. change::
- :tags:
- :tickets:
-
- eager loading has been internally overhauled to use aliases at all times. more
- complicated chains of eager loads can now be created without any need for explicit
- "use aliases"-type instructions. EagerLoader code is also much simpler now.
-
- .. change::
- :tags:
- :tickets:
-
- a new somewhat experimental flag "use_update" added to relations, indicates that
- this relationship should be handled by a second UPDATE statement, either after a
- primary INSERT or before a primary DELETE. handles circular row dependencies.
-
- .. change::
- :tags:
- :tickets:
-
- added exceptions module, all raised exceptions (except for some
- KeyError/AttributeError exceptions) descend from these classes.
-
- .. change::
- :tags:
- :tickets:
-
- fix to date types with MySQL, returned timedelta converted to datetime.time
-
- .. change::
- :tags:
- :tickets:
-
- two-phase objectstore.commit operations (i.e. begin/commit) now return a
- transactional object (SessionTrans), to more clearly indicate transaction boundaries.
-
- .. change::
- :tags:
- :tickets:
-
- Index object with create/drop support added to schema
-
- .. change::
- :tags:
- :tickets:
-
- fix to postgres, where it will explicitly pre-execute a PassiveDefault on a table
- if it is a primary key column, pursuant to the ongoing "we can't get inserted rows
- back from postgres" issue
-
- .. change::
- :tags:
- :tickets:
-
- change to information_schema query that gets back postgres table defs, now
- uses explicit JOIN keyword, since one user had faster performance with 8.1
-
- .. change::
- :tags:
- :tickets:
-
- fix to engine.process_defaults so it works correctly with a table that has
- different column name/column keys (changeset 982)
-
- .. change::
- :tags:
- :tickets:
-
- a column can only be attached to one table - this is now asserted
-
- .. change::
- :tags:
- :tickets:
-
- postgres time types descend from Time type
-
- .. change::
- :tags:
- :tickets:
-
- fix to alltests so that it runs types test (now named testtypes)
-
- .. change::
- :tags:
- :tickets:
-
- fix to Join object so that it correctly exports its foreign keys (cs 973)
-
- .. change::
- :tags:
- :tickets:
-
- creating relationships against mappers that use inheritance fixed (cs 973)
+++ /dev/null
-
-=============
-0.2 Changelog
-=============
-
-
-.. changelog::
- :version: 0.2.8
- :released: Tue Sep 05 2006
-
- .. change::
- :tags:
- :tickets:
-
- cleanup on connection methods + documentation. custom DBAPI
- arguments specified in query string, 'connect_args' argument
- to 'create_engine', or custom creation function via 'creator'
- function to 'create_engine'.
-
- .. change::
- :tags:
- :tickets: 274
-
- added "recycle" argument to Pool, is "pool_recycle" on create_engine,
- defaults to 3600 seconds; connections after this age will be closed and
- replaced with a new one, to handle db's that automatically close
- stale connections
-
- .. change::
- :tags:
- :tickets: 121
-
- changed "invalidate" semantics with pooled connection; will
- instruct the underlying connection record to reconnect the next
- time its called. "invalidate" will also automatically be called
- if any error is thrown in the underlying call to connection.cursor().
- this will hopefully allow the connection pool to reconnect to a
- database that had been stopped and started without restarting
- the connecting application
-
- .. change::
- :tags:
- :tickets:
-
- eesh ! the tutorial doctest was broken for quite some time.
-
- .. change::
- :tags:
- :tickets:
-
- add_property() method on mapper does a "compile all mappers"
- step in case the given property references a non-compiled mapper
- (as it did in the case of the tutorial !)
-
- .. change::
- :tags:
- :tickets: 277
-
- check for pg sequence already existing before create
-
- .. change::
- :tags:
- :tickets:
-
- if a contextual session is established via MapperExtension.get_session
- (as it is using the sessioncontext plugin, etc), a lazy load operation
- will use that session by default if the parent object is not
- persistent with a session already.
-
- .. change::
- :tags:
- :tickets:
-
- lazy loads will not fire off for an object that does not have a
- database identity (why?
- see https://www.sqlalchemy.org/trac/wiki/WhyDontForeignKeysLoadData)
-
- .. change::
- :tags:
- :tickets:
-
- unit-of-work does a better check for "orphaned" objects that are
- part of a "delete-orphan" cascade, for certain conditions where the
- parent isn't available to cascade from.
-
- .. change::
- :tags:
- :tickets:
-
- mappers can tell if one of their objects is an "orphan" based
- on interactions with the attribute package. this check is based
- on a status flag maintained for each relationship
- when objects are attached and detached from each other.
-
- .. change::
- :tags:
- :tickets:
-
- it is now invalid to declare a self-referential relationship with
- "delete-orphan" (as the abovementioned check would make them impossible
- to save)
-
- .. change::
- :tags:
- :tickets:
-
- improved the check for objects being part of a session when the
- unit of work seeks to flush() them as part of a relationship..
-
- .. change::
- :tags:
- :tickets: 280
-
- statement execution supports using the same BindParam
- object more than once in an expression; simplified handling of positional
- parameters. nice job by Bill Noon figuring out the basic idea.
-
- .. change::
- :tags:
- :tickets: 60, 71
-
- postgres reflection moved to use pg_schema tables, can be overridden
- with use_information_schema=True argument to create_engine.
-
- .. change::
- :tags:
- :tickets: 155
-
- added case_sensitive argument to MetaData, Table, Column, determines
- itself automatically based on if a parent schemaitem has a non-None
- setting for the flag, or if not, then whether the identifier name is all lower
- case or not. when set to True, quoting is applied to identifiers with mixed or
- uppercase identifiers. quoting is also applied automatically in all cases to
- identifiers that are known to be reserved words or contain other non-standard
- characters. various database dialects can override all of this behavior, but
- currently they are all using the default behavior. tested with postgres, mysql,
- sqlite, oracle. needs more testing with firebird, ms-sql. part of the ongoing
- work with
-
- .. change::
- :tags:
- :tickets:
-
- unit tests updated to run without any pysqlite installed; pool
- test uses a mock DBAPI
-
- .. change::
- :tags:
- :tickets: 281
-
- urls support escaped characters in passwords
-
- .. change::
- :tags:
- :tickets:
-
- added limit/offset to UNION queries (though not yet in oracle)
-
- .. change::
- :tags:
- :tickets:
-
- added "timezone=True" flag to DateTime and Time types. postgres
- so far will convert this to "TIME[STAMP] (WITH|WITHOUT) TIME ZONE",
- so that control over timezone presence is more controllable (psycopg2
- returns datetimes with tzinfo's if available, which can create confusion
- against datetimes that don't).
-
- .. change::
- :tags:
- :tickets: 287
-
- fix to using query.count() with distinct, \**kwargs with SelectResults
- count()
-
- .. change::
- :tags:
- :tickets: 289
-
- deregister Table from MetaData when autoload fails;
-
- .. change::
- :tags:
- :tickets: 293
-
- import of py2.5s sqlite3
-
- .. change::
- :tags:
- :tickets: 296
-
- unicode fix for startswith()/endswith()
-
-.. changelog::
- :version: 0.2.7
- :released: Sat Aug 12 2006
-
- .. change::
- :tags:
- :tickets:
-
- quoting facilities set up so that database-specific quoting can be
- turned on for individual table, schema, and column identifiers when
- used in all queries/creates/drops. Enabled via "quote=True" in
- Table or Column, as well as "quote_schema=True" in Table. Thanks to
- Aaron Spike for the excellent efforts.
-
- .. change::
- :tags:
- :tickets:
-
- assignmapper was setting is_primary=True, causing all sorts of mayhem
- by not raising an error when redundant mappers were set up, fixed
-
- .. change::
- :tags:
- :tickets:
-
- added allow_null_pks option to Mapper, allows rows where some
- primary key columns are null (i.e. when mapping to outer joins etc)
-
- .. change::
- :tags:
- :tickets:
-
- modification to unitofwork to not maintain ordering within the
- "new" list or within the UOWTask "objects" list; instead, new objects
- are tagged with an ordering identifier as they are registered as new
- with the session, and the INSERT statements are then sorted within the
- mapper save_obj. the INSERT ordering has basically been pushed all
- the way to the end of the flush cycle. that way the various sorts and
- organizations occurring within UOWTask (particularly the circular task
- sort) don't have to worry about maintaining order (which they weren't anyway)
-
- .. change::
- :tags:
- :tickets:
-
- fixed reflection of foreign keys to autoload the referenced table
- if it was not loaded already
-
- .. change::
- :tags:
- :tickets: 256
-
- - pass URL query string arguments to connect() function
-
- .. change::
- :tags:
- :tickets: 257
-
- - oracle boolean type
-
- .. change::
- :tags:
- :tickets:
-
- custom primary/secondary join conditions in a relation *will* be propagated
- to backrefs by default. specifying a backref() will override this behavior.
-
- .. change::
- :tags:
- :tickets:
-
- better check for ambiguous join conditions in sql.Join; propagates to a
- better error message in PropertyLoader (i.e. relation()/backref()) for when
- the join condition can't be reasonably determined.
-
- .. change::
- :tags:
- :tickets:
-
- sqlite creates ForeignKeyConstraint objects properly upon table
- reflection.
-
- .. change::
- :tags:
- :tickets: 224
-
- adjustments to pool stemming from changes made for.
- overflow counter should only be decremented if the connection actually
- succeeded. added a test script to attempt testing this.
-
- .. change::
- :tags:
- :tickets:
-
- fixed mysql reflection of default values to be PassiveDefault
-
- .. change::
- :tags:
- :tickets: 263, 264
-
- added reflected 'tinyint', 'mediumint' type to MS-SQL.
-
- .. change::
- :tags:
- :tickets:
-
- SingletonThreadPool has a size and does a cleanup pass, so that
- only a given number of thread-local connections stay around (needed
- for sqlite applications that dispose of threads en masse)
-
- .. change::
- :tags:
- :tickets: 267, 265
-
- fixed small pickle bug(s) with lazy loaders
-
- .. change::
- :tags:
- :tickets:
-
- fixed possible error in mysql reflection where certain versions
- return an array instead of string for SHOW CREATE TABLE call
-
- .. change::
- :tags:
- :tickets: 1770
-
- fix to lazy loads when mapping to joins
-
- .. change::
- :tags:
- :tickets:
-
- all create()/drop() calls have a keyword argument of "connectable".
- "engine" is deprecated.
-
- .. change::
- :tags:
- :tickets:
-
- fixed ms-sql connect() to work with adodbapi
-
- .. change::
- :tags:
- :tickets:
-
- added "nowait" flag to Select()
-
- .. change::
- :tags:
- :tickets: 271
-
- inheritance check uses issubclass() instead of direct __mro__ check
- to make sure class A inherits from B, allowing mapper inheritance to more
- flexibly correspond to class inheritance
-
- .. change::
- :tags:
- :tickets: 252
-
- SelectResults will use a subselect, when calling an aggregate (i.e.
- max, min, etc.) on a SelectResults that has an ORDER BY clause
-
- .. change::
- :tags:
- :tickets: 269
-
- fixes to types so that database-specific types more easily used;
- fixes to mysql text types to work with this methodology
-
- .. change::
- :tags:
- :tickets:
-
- some fixes to sqlite date type organization
-
- .. change::
- :tags:
- :tickets: 263
-
- added MSTinyInteger to MS-SQL
-
-.. changelog::
- :version: 0.2.6
- :released: Thu Jul 20 2006
-
- .. change::
- :tags:
- :tickets: 76
-
- big overhaul to schema to allow truly composite primary and foreign
- key constraints, via new ForeignKeyConstraint and PrimaryKeyConstraint
- objects.
- Existing methods of primary/foreign key creation have not been changed
- but use these new objects behind the scenes. table creation
- and reflection is now more table oriented rather than column oriented.
-
- .. change::
- :tags:
- :tickets:
-
- overhaul to MapperExtension calling scheme, wasn't working very well
- previously
-
- .. change::
- :tags:
- :tickets:
-
- tweaks to ActiveMapper, supports self-referential relationships
-
- .. change::
- :tags:
- :tickets:
-
- slight rearrangement to objectstore (in activemapper/threadlocal)
- so that the SessionContext is referenced by '.context' instead
- of subclassed directly.
-
- .. change::
- :tags:
- :tickets:
-
- activemapper will use threadlocal's objectstore if the mod is
- activated when activemapper is imported
-
- .. change::
- :tags:
- :tickets:
-
- small fix to URL regexp to allow filenames with '@' in them
-
- .. change::
- :tags:
- :tickets:
-
- fixes to Session expunge/update/etc...needs more cleanup.
-
- .. change::
- :tags:
- :tickets:
-
- select_table mappers *still* weren't always compiling
-
- .. change::
- :tags:
- :tickets:
-
- fixed up Boolean datatype
-
- .. change::
- :tags:
- :tickets:
-
- added count()/count_by() to list of methods proxied by assignmapper;
- this also adds them to activemapper
-
- .. change::
- :tags:
- :tickets:
-
- connection exceptions wrapped in DBAPIError
-
- .. change::
- :tags:
- :tickets:
-
- ActiveMapper now supports autoloading column definitions from the
- database if you supply a __autoload__ = True attribute in your
- mapping inner-class. Currently this does not support reflecting
- any relationships.
-
- .. change::
- :tags:
- :tickets:
-
- deferred column load could screw up the connection status in
- a flush() under some circumstances, this was fixed
-
- .. change::
- :tags:
- :tickets:
-
- expunge() was not working with cascade, fixed.
-
- .. change::
- :tags:
- :tickets:
-
- potential endless loop in cascading operations fixed.
-
- .. change::
- :tags:
- :tickets:
-
- added "synonym()" function, applied to properties to have a
- propname the same as another, for the purposes of overriding props
- and allowing the original propname to be accessible in select_by().
-
- .. change::
- :tags:
- :tickets:
-
- fix to typing in clause construction which specifically helps
- type issues with polymorphic_union (CAST/ColumnClause propagates
- its type to proxy columns)
-
- .. change::
- :tags:
- :tickets:
-
- mapper compilation work ongoing, someday it'll work....moved
- around the initialization of MapperProperty objects to be after
- all mappers are created to better handle circular compilations.
- do_init() method is called on all properties now which are more
- aware of their "inherited" status if so.
-
- .. change::
- :tags:
- :tickets:
-
- eager loads explicitly disallowed on self-referential relationships, or
- relationships to an inheriting mapper (which is also self-referential)
-
- .. change::
- :tags:
- :tickets: 244
-
- reduced bind param size in query._get to appease the picky oracle
-
- .. change::
- :tags:
- :tickets: 234
-
- added 'checkfirst' argument to table.create()/table.drop(), as
- well as table.exists()
-
- .. change::
- :tags:
- :tickets: 245
-
- some other ongoing fixes to inheritance
-
- .. change::
- :tags:
- :tickets:
-
- attribute/backref/orphan/history-tracking tweaks as usual...
-
-.. changelog::
- :version: 0.2.5
- :released: Sat Jul 08 2006
-
- .. change::
- :tags:
- :tickets:
-
- fixed endless loop bug in select_by(), if the traversal hit
- two mappers that referenced each other
-
- .. change::
- :tags:
- :tickets:
-
- upgraded all unittests to insert './lib/' into sys.path,
- working around new setuptools PYTHONPATH-killing behavior
-
- .. change::
- :tags:
- :tickets:
-
- further fixes with attributes/dependencies/etc....
-
- .. change::
- :tags:
- :tickets:
-
- improved error handling for when DynamicMetaData is not connected
-
- .. change::
- :tags:
- :tickets:
-
- MS-SQL support largely working (tested with pymssql)
-
- .. change::
- :tags:
- :tickets:
-
- ordering of UPDATE and DELETE statements within groups is now
- in order of primary key values, for more deterministic ordering
-
- .. change::
- :tags:
- :tickets:
-
- after_insert/delete/update mapper extensions now called per object,
- not per-object-per-table
-
- .. change::
- :tags:
- :tickets:
-
- further fixes/refactorings to mapper compilation
-
-.. changelog::
- :version: 0.2.4
- :released: Tue Jun 27 2006
-
- .. change::
- :tags:
- :tickets:
-
- try/except when the mapper sets init.__name__ on a mapped class,
- supports python 2.3
-
- .. change::
- :tags:
- :tickets:
-
- fixed bug where threadlocal engine would still autocommit
- despite a transaction in progress
-
- .. change::
- :tags:
- :tickets:
-
- lazy load and deferred load operations require the parent object
- to be in a Session to do the operation; whereas before the operation
- would just return a blank list or None, it now raises an exception.
-
- .. change::
- :tags:
- :tickets:
-
- Session.update() is slightly more lenient if the session to which
- the given object was formerly attached to was garbage collected;
- otherwise still requires you explicitly remove the instance from
- the previous Session.
-
- .. change::
- :tags:
- :tickets:
-
- fixes to mapper compilation, checking for more error conditions
-
- .. change::
- :tags:
- :tickets:
-
- small fix to eager loading combined with ordering/limit/offset
-
- .. change::
- :tags:
- :tickets: 206
-
- utterly remarkable: added a single space between 'CREATE TABLE'
- and '(<the rest of it>' since *that's how MySQL indicates a non-
- reserved word tablename.....*
-
- .. change::
- :tags:
- :tickets:
-
- more fixes to inheritance, related to many-to-many relations
- properly saving
-
- .. change::
- :tags:
- :tickets:
-
- fixed bug when specifying explicit module to mysql dialect
-
- .. change::
- :tags:
- :tickets:
-
- when QueuePool times out it raises a TimeoutError instead of
- erroneously making another connection
-
- .. change::
- :tags:
- :tickets:
-
- Queue.Queue usage in pool has been replaced with a locally
- modified version (works in py2.3/2.4!) that uses a threading.RLock
- for a mutex. this is to fix a reported case where a ConnectionFairy's
- __del__() method got called within the Queue's get() method, which
- then returns its connection to the Queue via the put() method,
- causing a reentrant hang unless threading.RLock is used.
-
- .. change::
- :tags:
- :tickets:
-
- postgres will not place SERIAL keyword on a primary key column
- if it has a foreign key constraint
-
- .. change::
- :tags:
- :tickets: 221
-
- cursor() method on ConnectionFairy allows db-specific extension
- arguments to be propagated
-
- .. change::
- :tags:
- :tickets: 225
-
- lazy load bind params properly propagate column type
-
- .. change::
- :tags:
- :tickets:
-
- new MySQL types: MSEnum, MSTinyText, MSMediumText, MSLongText, etc.
- more support for MS-specific length/precision params in numeric types
- patch courtesy Mike Bernson
-
- .. change::
- :tags:
- :tickets: 224
-
- some fixes to connection pool invalidate()
-
-.. changelog::
- :version: 0.2.3
- :released: Sat Jun 17 2006
-
- .. change::
- :tags:
- :tickets:
-
- overhaul to mapper compilation to be deferred. this allows mappers
- to be constructed in any order, and their relationships to each
- other are compiled when the mappers are first used.
-
- .. change::
- :tags:
- :tickets:
-
- fixed a pretty big speed bottleneck in cascading behavior particularly
- when backrefs were in use
-
- .. change::
- :tags:
- :tickets:
-
- the attribute instrumentation module has been completely rewritten; its
- now a large degree simpler and clearer, slightly faster. the "history"
- of an attribute is no longer micromanaged with each change and is
- instead part of a "CommittedState" object created when the
- instance is first loaded. HistoryArraySet is gone, the behavior of
- list attributes is now more open ended (i.e. they're not sets anymore).
-
- .. change::
- :tags:
- :tickets:
-
- py2.4 "set" construct used internally, falls back to sets.Set when
- "set" not available/ordering is needed.
-
- .. change::
- :tags:
- :tickets:
-
- fix to transaction control, so that repeated rollback() calls
- don't fail (was failing pretty badly when flush() would raise
- an exception in a larger try/except transaction block)
-
- .. change::
- :tags:
- :tickets: 151
-
- "foreignkey" argument to relation() can also be a list. fixed
- auto-foreignkey detection
-
- .. change::
- :tags:
- :tickets:
-
- fixed bug where tables with schema names weren't getting indexed in
- the MetaData object properly
-
- .. change::
- :tags:
- :tickets: 207
-
- fixed bug where Column with redefined "key" property wasn't getting
- type conversion happening in the ResultProxy
-
- .. change::
- :tags:
- :tickets:
-
- fixed 'port' attribute of URL to be an integer if present
-
- .. change::
- :tags:
- :tickets:
-
- fixed old bug where if a many-to-many table mapped as "secondary"
- had extra columns, delete operations didn't work
-
- .. change::
- :tags:
- :tickets:
-
- bugfixes for mapping against UNION queries
-
- .. change::
- :tags:
- :tickets:
-
- fixed incorrect exception class thrown when no DB driver present
-
- .. change::
- :tags:
- :tickets: 138
-
- added NonExistentTable exception thrown when reflecting a table
- that doesn't exist
-
- .. change::
- :tags:
- :tickets:
-
- small fix to ActiveMapper regarding one-to-one backrefs, other
- refactorings
-
- .. change::
- :tags:
- :tickets:
-
- overridden constructor in mapped classes gets __name__ and
- __doc__ from the original class
-
- .. change::
- :tags:
- :tickets: 200
-
- fixed small bug in selectresult.py regarding mapper extension
-
- .. change::
- :tags:
- :tickets:
-
- small tweak to cascade_mappers, not very strongly supported
- function at the moment
-
- .. change::
- :tags:
- :tickets: 202
-
- some fixes to between(), column.between() to propagate typing
- information better
-
- .. change::
- :tags:
- :tickets: 203
-
- if an object fails to be constructed, is not added to the
- session
-
- .. change::
- :tags:
- :tickets:
-
- CAST function has been made into its own clause object with
- its own compilation function in ansicompiler; allows MySQL
- to silently ignore most CAST calls since MySQL
- seems to only support the standard CAST syntax with Date types.
- MySQL-compatible CAST support for strings, ints, etc. a TODO
-
-.. changelog::
- :version: 0.2.2
- :released: Mon Jun 05 2006
-
- .. change::
- :tags:
- :tickets: 190
-
- big improvements to polymorphic inheritance behavior, enabling it
- to work with adjacency list table structures
-
- .. change::
- :tags:
- :tickets:
-
- major fixes and refactorings to inheritance relationships overall,
- more unit tests
-
- .. change::
- :tags:
- :tickets:
-
- fixed "echo_pool" flag on create_engine()
-
- .. change::
- :tags:
- :tickets:
-
- fix to docs, removed incorrect info that close() is unsafe to use
- with threadlocal strategy (its totally safe !)
-
- .. change::
- :tags:
- :tickets: 188
-
- create_engine() can take URLs as string or unicode
-
- .. change::
- :tags:
- :tickets:
-
- firebird support partially completed;
- thanks to James Ralston and Brad Clements for their efforts.
-
- .. change::
- :tags:
- :tickets:
-
- Oracle url translation was broken, fixed, will feed host/port/sid
- into cx_oracle makedsn() if 'database' field is present, else uses
- straight TNS name from the 'host' field
-
- .. change::
- :tags:
- :tickets:
-
- fix to using unicode criterion for query.get()/query.load()
-
- .. change::
- :tags:
- :tickets:
-
- count() function on selectables now uses table primary key or
- first column instead of "1" for criterion, also uses label "rowcount"
- instead of "count".
-
- .. change::
- :tags:
- :tickets:
-
- got rudimental "mapping to multiple tables" functionality cleaned up,
- more correctly documented
-
- .. change::
- :tags:
- :tickets:
-
- restored global_connect() function, attaches to a DynamicMetaData
- instance called "default_metadata". leaving MetaData arg to Table
- out will use the default metadata.
-
- .. change::
- :tags:
- :tickets:
-
- fixes to session cascade behavior, entity_name propagation
-
- .. change::
- :tags:
- :tickets:
-
- reorganized unittests into subdirectories
-
- .. change::
- :tags:
- :tickets:
-
- more fixes to threadlocal connection nesting patterns
-
-.. changelog::
- :version: 0.2.1
- :released: Mon May 29 2006
-
- .. change::
- :tags:
- :tickets:
-
- "pool" argument to create_engine() properly propagates
-
- .. change::
- :tags:
- :tickets:
-
- fixes to URL, raises exception if not parsed, does not pass blank
- fields along to the DB connect string (a string such as
- user:host@/db was breaking on postgres)
-
- .. change::
- :tags:
- :tickets:
-
- small fixes to Mapper when it inserts and tries to get
- new primary key values back
-
- .. change::
- :tags:
- :tickets:
-
- rewrote half of TLEngine, the ComposedSQLEngine used with
- 'strategy="threadlocal"'. it now properly implements engine.begin()/
- engine.commit(), which nest fully with connection.begin()/trans.commit().
- added about six unittests.
-
- .. change::
- :tags:
- :tickets:
-
- major "duh" in pool.Pool, forgot to put back the WeakValueDictionary.
- unittest which was supposed to check for this was also silently missing
- it. fixed unittest to ensure that ConnectionFairy properly falls out
- of scope.
-
- .. change::
- :tags:
- :tickets:
-
- placeholder dispose() method added to SingletonThreadPool, doesn't
- do anything yet
-
- .. change::
- :tags:
- :tickets:
-
- rollback() is automatically called when an exception is raised,
- but only if there's no transaction in process (i.e. works more like
- autocommit).
-
- .. change::
- :tags:
- :tickets:
-
- fixed exception raise in sqlite if no sqlite module present
-
- .. change::
- :tags:
- :tickets:
-
- added extra example detail for association object doc
-
- .. change::
- :tags:
- :tickets:
-
- Connection adds checks for already being closed
-
-.. changelog::
- :version: 0.2.0
- :released: Sat May 27 2006
-
- .. change::
- :tags:
- :tickets:
-
- overhaul to Engine system so that what was formerly the SQLEngine
- is now a ComposedSQLEngine which consists of a variety of components,
- including a Dialect, ConnectionProvider, etc. This impacted all the
- db modules as well as Session and Mapper.
-
- .. change::
- :tags:
- :tickets:
-
- create_engine now takes only RFC-1738-style strings:
- ``driver://user:password@host:port/database``
-
- **update** this format is generally but not exactly RFC-1738,
- including that underscores, not dashes or periods, are accepted in the
- "scheme" portion.
-
- .. change::
- :tags:
- :tickets: 152
-
- total rewrite of connection-scoping methodology, Connection objects
- can now execute clause elements directly, added explicit "close" as
- well as support throughout Engine/ORM to handle closing properly,
- no longer relying upon __del__ internally to return connections
- to the pool.
-
- .. change::
- :tags:
- :tickets:
-
- overhaul to Session interface and scoping. uses hibernate-style
- methods, including query(class), save(), save_or_update(), etc.
- no threadlocal scope is installed by default. Provides a binding
- interface to specific Engines and/or Connections so that underlying
- Schema objects do not need to be bound to an Engine. Added a basic
- SessionTransaction object that can simplistically aggregate transactions
- across multiple engines.
-
- .. change::
- :tags:
- :tickets:
-
- overhaul to mapper's dependency and "cascade" behavior; dependency logic
- factored out of properties.py into a separate module "dependency.py".
- "cascade" behavior is now explicitly controllable, proper implementation
- of "delete", "delete-orphan", etc. dependency system can now determine at
- flush time if a child object has a parent or not so that it makes better
- decisions on how that child should be updated in the DB with regards to deletes.
-
- .. change::
- :tags:
- :tickets:
-
- overhaul to Schema to build upon MetaData object instead of an Engine.
- Entire SQL/Schema system can be used with no Engines whatsoever, executed
- solely by an explicit Connection object. the "bound" methodology exists via the
- BoundMetaData for schema objects. ProxyEngine is generally not needed
- anymore and is replaced by DynamicMetaData.
-
- .. change::
- :tags:
- :tickets: 167
-
- true polymorphic behavior implemented, fixes
-
- .. change::
- :tags:
- :tickets: 147
-
- "oid" system has been totally moved into compile-time behavior;
- if they are used in an order_by where they are not available, the order_by
- doesn't get compiled, fixes
-
- .. change::
- :tags:
- :tickets:
-
- overhaul to packaging; "mapping" is now "orm", "objectstore" is now
- "session", the old "objectstore" namespace gets loaded in via the
- "threadlocal" mod if used
-
- .. change::
- :tags:
- :tickets:
-
- mods now called in via "import <modname>". extensions favored over
- mods as mods are globally-monkeypatching
-
- .. change::
- :tags:
- :tickets: 154
-
- fix to add_property so that it propagates properties to inheriting
- mappers
-
- .. change::
- :tags:
- :tickets:
-
- backrefs create themselves against primary mapper of its originating
- property, primary/secondary join arguments can be specified to override.
- helps their usage with polymorphic mappers
-
- .. change::
- :tags:
- :tickets: 31
-
- "table exists" function has been implemented
-
- .. change::
- :tags:
- :tickets: 98
-
- "create_all/drop_all" added to MetaData object
-
- .. change::
- :tags:
- :tickets:
-
- improvements and fixes to topological sort algorithm, as well as more
- unit tests
-
- .. change::
- :tags:
- :tickets:
-
- tutorial page added to docs which also can be run with a custom doctest
- runner to ensure its properly working. docs generally overhauled to
- deal with new code patterns
-
- .. change::
- :tags:
- :tickets:
-
- many more fixes, refactorings.
-
- .. change::
- :tags:
- :tickets:
-
- migration guide is available on the Wiki at
- https://www.sqlalchemy.org/trac/wiki/02Migration
+++ /dev/null
-
-=============
-0.3 Changelog
-=============
-
-
-.. changelog::
- :version: 0.3.11
- :released: Sun Oct 14 2007
-
- .. change::
- :tags: sql
- :tickets:
-
- tweak DISTINCT precedence for clauses like
- `func.count(t.c.col.distinct())`
-
- .. change::
- :tags: sql
- :tickets: 719
-
- Fixed detection of internal '$' characters in :bind$params
-
- .. change::
- :tags: sql
- :tickets: 768
-
- don't assume join criterion consists only of column objects
-
- .. change::
- :tags: sql
- :tickets: 764
-
- adjusted operator precedence of NOT to match '==' and others, so that
- ~(x==y) produces NOT (x=y), which is compatible with MySQL < 5.0
- (doesn't like "NOT x=y")
-
- .. change::
- :tags: orm
- :tickets: 687
-
- added a check for joining from A->B using join(), along two
- different m2m tables. this raises an error in 0.3 but is
- possible in 0.4 when aliases are used.
-
- .. change::
- :tags: orm
- :tickets:
-
- fixed small exception throw bug in Session.merge()
-
- .. change::
- :tags: orm
- :tickets:
-
- fixed bug where mapper, being linked to a join where one table had
- no PK columns, would not detect that the joined table had no PK.
-
- .. change::
- :tags: orm
- :tickets: 769
-
- fixed bugs in determining proper sync clauses from custom inherit
- conditions
-
- .. change::
- :tags: orm
- :tickets: 813
-
- backref remove object operation doesn't fail if the other-side
- collection doesn't contain the item, supports noload collections
-
- .. change::
- :tags: engine
- :tickets:
-
- fixed another occasional race condition which could occur
- when using pool with threadlocal setting
-
- .. change::
- :tags: mysql
- :tickets:
-
- fixed specification of YEAR columns when generating schema
-
- .. change::
- :tags: mssql
- :tickets: 679
-
- added support for TIME columns (simulated using DATETIME)
-
- .. change::
- :tags: mssql
- :tickets: 721
-
- added support for BIGINT, MONEY, SMALLMONEY, UNIQUEIDENTIFIER and
- SQL_VARIANT
-
- .. change::
- :tags: mssql
- :tickets: 684
-
- index names are now quoted when dropping from reflected tables
-
- .. change::
- :tags: mssql
- :tickets:
-
- can now specify a DSN for PyODBC, using a URI like mssql:///?dsn=bob
-
- .. change::
- :tags: postgres
- :tickets:
-
- when reflecting tables from alternate schemas, the "default" placed upon
- the primary key, i.e. usually a sequence name, has the "schema" name
- unconditionally quoted, so that schema names which need quoting are fine.
- its slightly unnecessary for schema names which don't need quoting
- but not harmful.
-
- .. change::
- :tags: sqlite
- :tickets:
-
- passthrough for stringified dates
-
- .. change::
- :tags: firebird
- :tickets:
-
- supports_sane_rowcount() set to False due to ticket #370 (right way).
-
- .. change::
- :tags: firebird
- :tickets:
-
- fixed reflection of Column's nullable property.
-
- .. change::
- :tags: oracle
- :tickets: 622, 751
-
- removed LONG_STRING, LONG_BINARY from "binary" types, so type objects
- don't try to read their values as LOB.
-
-.. changelog::
- :version: 0.3.10
- :released: Fri Jul 20 2007
-
- .. change::
- :tags: general
- :tickets:
-
- a new mutex that was added in 0.3.9 causes the pool_timeout
- feature to fail during a race condition; threads would
- raise TimeoutError immediately with no delay if many threads
- push the pool into overflow at the same time. this issue has been
- fixed.
-
- .. change::
- :tags: sql
- :tickets:
-
- got connection-bound metadata to work with implicit execution
-
- .. change::
- :tags: sql
- :tickets: 667
-
- foreign key specs can have any character in their identifiers
-
- .. change::
- :tags: sql
- :tickets: 664
-
- added commutativity-awareness to binary clause comparisons to
- each other, improves ORM lazy load optimization
-
- .. change::
- :tags: orm
- :tickets:
-
- cleanup to connection-bound sessions, SessionTransaction
-
- .. change::
- :tags: postgres
- :tickets: 571
-
- fixed max identifier length (63)
-
-.. changelog::
- :version: 0.3.9
- :released: Sun Jul 15 2007
-
- .. change::
- :tags: general
- :tickets: 607
-
- better error message for NoSuchColumnError
-
- .. change::
- :tags: general
- :tickets: 428
-
- finally figured out how to get setuptools version in, available
- as sqlalchemy.__version__
-
- .. change::
- :tags: general
- :tickets:
-
- the various "engine" arguments, such as "engine", "connectable",
- "engine_or_url", "bind_to", etc. are all present, but deprecated.
- they all get replaced by the single term "bind". you also
- set the "bind" of MetaData using
- metadata.bind = <engine or connection>
-
- .. change::
- :tags: ext
- :tickets:
-
- iteration over dict association proxies is now dict-like, not
- InstrumentedList-like (e.g. over keys instead of values)
-
- .. change::
- :tags: ext
- :tickets: 597
-
- association proxies no longer bind tightly to source collections, and are constructed with a thunk instead
-
- .. change::
- :tags: ext
- :tickets:
-
- added selectone_by() to assignmapper
-
- .. change::
- :tags: orm
- :tickets:
-
- forwards-compatibility with 0.4: added one(), first(), and
- all() to Query. almost all Query functionality from 0.4 is
- present in 0.3.9 for forwards-compat purposes.
-
- .. change::
- :tags: orm
- :tickets:
-
- reset_joinpoint() really really works this time, promise ! lets
- you re-join from the root:
- query.join(['a', 'b']).filter(<crit>).reset_joinpoint().\
- join(['a', 'c']).filter(<some other crit>).all()
- in 0.4 all join() calls start from the "root"
-
- .. change::
- :tags: orm
- :tickets: 613
-
- added synchronization to the mapper() construction step, to avoid
- thread collisions when pre-existing mappers are compiling in a
- different thread
-
- .. change::
- :tags: orm
- :tickets:
-
- a warning is issued by Mapper when two primary key columns of the
- same name are munged into a single attribute. this happens frequently
- when mapping to joins (or inheritance).
-
- .. change::
- :tags: orm
- :tickets: 598
-
- synonym() properties are fully supported by all Query joining/
- with_parent operations
-
- .. change::
- :tags: orm
- :tickets:
-
- fixed very stupid bug when deleting items with many-to-many
- uselist=False relations
-
- .. change::
- :tags: orm
- :tickets:
-
- remember all that stuff about polymorphic_union ? for
- joined table inheritance ? Funny thing...
- You sort of don't need it for joined table inheritance, you
- can just string all the tables together via outerjoin().
- The UNION still applies if concrete tables are involved,
- though (since nothing to join them on).
-
- .. change::
- :tags: orm
- :tickets:
-
- small fix to eager loading to better work with eager loads
- to polymorphic mappers that are using a straight "outerjoin"
- clause
-
- .. change::
- :tags: sql
- :tickets:
-
- ForeignKey to a table in a schema that's not the default schema
- requires the schema to be explicit; i.e. ForeignKey('alt_schema.users.id')
-
- .. change::
- :tags: sql
- :tickets:
-
- MetaData can now be constructed with an engine or url as the first
- argument, just like BoundMetaData
-
- .. change::
- :tags: sql
- :tickets:
-
- BoundMetaData is now deprecated, and MetaData is a direct substitute.
-
- .. change::
- :tags: sql
- :tickets:
-
- DynamicMetaData has been renamed to ThreadLocalMetaData. the
- DynamicMetaData name is deprecated and is an alias for ThreadLocalMetaData
- or a regular MetaData if threadlocal=False
-
- .. change::
- :tags: sql
- :tickets:
-
- composite primary key is represented as a non-keyed set to allow for
- composite keys consisting of cols with the same name; occurs within a
- Join. helps inheritance scenarios formulate correct PK.
-
- .. change::
- :tags: sql
- :tickets: 185
-
- improved ability to get the "correct" and most minimal set of primary key
- columns from a join, equating foreign keys and otherwise equated columns.
- this is also mostly to help inheritance scenarios formulate the best
- choice of primary key columns.
-
- .. change::
- :tags: sql
- :tickets:
-
- added 'bind' argument to Sequence.create()/drop(), ColumnDefault.execute()
-
- .. change::
- :tags: sql
- :tickets: 650
-
- columns can be overridden in a reflected table with a "key"
- attribute different than the column's name, including for primary key
- columns
-
- .. change::
- :tags: sql
- :tickets: 657
-
- fixed "ambiguous column" result detection, when dupe col names exist
- in a result
-
- .. change::
- :tags: sql
- :tickets:
-
- some enhancements to "column targeting", the ability to match a column
- to a "corresponding" column in another selectable. this affects mostly
- ORM ability to map to complex joins
-
- .. change::
- :tags: sql
- :tickets: 619
-
- MetaData and all SchemaItems are safe to use with pickle. slow
- table reflections can be dumped into a pickled file to be reused later.
- Just reconnect the engine to the metadata after unpickling.
-
- .. change::
- :tags: sql
- :tickets:
-
- added a mutex to QueuePool's "overflow" calculation to prevent a race
- condition that can bypass max_overflow
-
- .. change::
- :tags: sql
- :tickets: 623
-
- fixed grouping of compound selects to give correct results. will break
- on sqlite in some cases, but those cases were producing incorrect
- results anyway, sqlite doesn't support grouped compound selects
-
- .. change::
- :tags: sql
- :tickets: 620
-
- fixed precedence of operators so that parenthesis are correctly applied
-
- .. change::
- :tags: sql
- :tickets: 545
-
- calling <column>.in_() (i.e. with no arguments) will return
- "CASE WHEN (<column> IS NULL) THEN NULL ELSE 0 END = 1)", so that
- NULL or False is returned in all cases, rather than throwing an error
-
- .. change::
- :tags: sql
- :tickets:
-
- fixed "where"/"from" criterion of select() to accept a unicode string
- in addition to regular string - both convert to text()
-
- .. change::
- :tags: sql
- :tickets: 558
-
- added standalone distinct() function in addition to column.distinct()
-
- .. change::
- :tags: sql
- :tickets:
-
- result.last_inserted_ids() should return a list that is identically
- sized to the primary key constraint of the table. values that were
- "passively" created and not available via cursor.lastrowid will be None.
-
- .. change::
- :tags: sql
- :tickets: 589
-
- long-identifier detection fixed to use > rather than >= for
- max ident length
-
- .. change::
- :tags: sql
- :tickets: 593
-
- fixed bug where selectable.corresponding_column(selectable.c.col)
- would not return selectable.c.col, if the selectable is a join
- of a table and another join involving the same table. messed
- up ORM decision making
-
- .. change::
- :tags: sql
- :tickets: 595
-
- added Interval type to types.py
-
- .. change::
- :tags: mysql
- :tickets: 625
-
- fixed catching of some errors that imply a dropped connection
-
- .. change::
- :tags: mysql
- :tickets: 624
-
- fixed escaping of the modulo operator
-
- .. change::
- :tags: mysql
- :tickets: 590
-
- added 'fields' to reserved words
-
- .. change::
- :tags: mysql
- :tickets:
-
- various reflection enhancement/fixes
-
- .. change::
- :tags: oracle
- :tickets: 604
-
- datetime fixes: got subsecond TIMESTAMP to work,
- added OracleDate which supports types.Date with only year/month/day
-
- .. change::
- :tags: oracle
- :tickets:
-
- added dialect flag "auto_convert_lobs", defaults to True; will cause any
- LOB objects detected in a result set to be forced into OracleBinary
- so that the LOB is read() automatically, if no typemap was present
- (i.e., if a textual execute() was issued).
-
- .. change::
- :tags: oracle
- :tickets: 624
-
- mod operator '%' produces MOD
-
- .. change::
- :tags: oracle
- :tickets: 542
-
- converts cx_oracle datetime objects to Python datetime.datetime when
- Python 2.3 used
-
- .. change::
- :tags: oracle
- :tickets:
-
- fixed unicode conversion in Oracle TEXT type
-
- .. change::
- :tags: postgres
- :tickets: 624
-
- fixed escaping of the modulo operator
-
- .. change::
- :tags: postgres
- :tickets: 570
-
- added support for reflection of domains
-
- .. change::
- :tags: postgres
- :tickets:
-
- types which are missing during reflection resolve to Null type
- instead of raising an error
-
- .. change::
- :tags: postgres
- :tickets:
-
- the fix in "schema" above fixes reflection of foreign keys from an
- alt-schema table to a public schema table
-
- .. change::
- :tags: sqlite
- :tickets:
-
- rearranged dialect initialization so it has time to warn about pysqlite1
- being too old.
-
- .. change::
- :tags: sqlite
- :tickets:
-
- sqlite better handles datetime/date/time objects mixed and matched
- with various Date/Time/DateTime columns
-
- .. change::
- :tags: sqlite
- :tickets: 603
-
- string PK column inserts don't get overwritten with OID
-
- .. change::
- :tags: mssql
- :tickets: 634
-
- fix port option handling for pyodbc
-
- .. change::
- :tags: mssql
- :tickets:
-
- now able to reflect start and increment values for identity columns
-
- .. change::
- :tags: mssql
- :tickets:
-
- preliminary support for using scope_identity() with pyodbc
-
-.. changelog::
- :version: 0.3.8
- :released: Sat Jun 02 2007
-
- .. change::
- :tags: engines
- :tickets:
-
- added detach() to Connection, allows underlying DBAPI connection
- to be detached from its pool, closing on dereference/close()
- instead of being reused by the pool.
-
- .. change::
- :tags: engines
- :tickets:
-
- added invalidate() to Connection, immediately invalidates the
- Connection and its underlying DBAPI connection.
-
- .. change::
- :tags: sql
- :tickets:
-
- _Label class overrides compare_self to return its ultimate
- object. meaning, if you say someexpr.label('foo') == 5, it
- produces the correct "someexpr == 5".
-
- .. change::
- :tags: sql
- :tickets:
-
- _Label propagates "_hide_froms()" so that scalar selects
- behave more properly with regards to FROM clause #574
-
- .. change::
- :tags: sql
- :tickets:
-
- fix to long name generation when using oid_column as an order by
- (oids used heavily in mapper queries)
-
- .. change::
- :tags: sql
- :tickets:
-
- significant speed improvement to ResultProxy, pre-caches
- TypeEngine dialect implementations and saves on function calls
- per column
-
- .. change::
- :tags: sql
- :tickets:
-
- parenthesis are applied to clauses via a new _Grouping
- construct. uses operator precedence to more intelligently apply
- parenthesis to clauses, provides cleaner nesting of clauses
- (doesn't mutate clauses placed in other clauses, i.e. no 'parens'
- flag)
-
- .. change::
- :tags: sql
- :tickets:
-
- added 'modifier' keyword, works like func.<foo> except does not
- add parenthesis. e.g. select([modifier.DISTINCT(...)]) etc.
-
- .. change::
- :tags: sql
- :tickets: 578
-
- removed "no group by's in a select that's part of a UNION"
- restriction
-
- .. change::
- :tags: orm
- :tickets:
-
- added reset_joinpoint() method to Query, moves the "join point"
- back to the starting mapper. 0.4 will change the behavior of
- join() to reset the "join point" in all cases so this is an
- interim method. for forwards compatibility, ensure joins across
- multiple relations are specified using a single join(), i.e.
- join(['a', 'b', 'c']).
-
- .. change::
- :tags: orm
- :tickets:
-
- fixed bug in query.instances() that wouldn't handle more than
- on additional mapper or one additional column.
-
- .. change::
- :tags: orm
- :tickets:
-
- "delete-orphan" no longer implies "delete". ongoing effort to
- separate the behavior of these two operations.
-
- .. change::
- :tags: orm
- :tickets:
-
- many-to-many relationships properly set the type of bind params
- for delete operations on the association table
-
- .. change::
- :tags: orm
- :tickets:
-
- many-to-many relationships check that the number of rows deleted
- from the association table by a delete operation matches the
- expected results
-
- .. change::
- :tags: orm
- :tickets:
-
- session.get() and session.load() propagate \**kwargs through to
- query
-
- .. change::
- :tags: orm
- :tickets: 577
-
- fix to polymorphic query which allows the original
- polymorphic_union to be embedded into a correlated subquery
-
- .. change::
- :tags: orm
- :tickets:
-
- fix to select_by(<propname>=<object instance>) -style joins in
- conjunction with many-to-many relationships, bug introduced in
- r2556
-
- .. change::
- :tags: orm
- :tickets:
-
- the "primary_key" argument to mapper() is propagated to the
- "polymorphic" mapper. primary key columns in this list get
- normalized to that of the mapper's local table.
-
- .. change::
- :tags: orm
- :tickets:
-
- restored logging of "lazy loading clause" under
- sa.orm.strategies logger, got removed in 0.3.7
-
- .. change::
- :tags: orm
- :tickets:
-
- improved support for eagerloading of properties off of mappers
- that are mapped to select() statements; i.e. eagerloader is
- better at locating the correct selectable with which to attach
- its LEFT OUTER JOIN.
-
- .. change::
- :tags: mysql
- :tickets:
-
- Nearly all MySQL column types are now supported for declaration
- and reflection. Added NCHAR, NVARCHAR, VARBINARY, TINYBLOB,
- LONGBLOB, YEAR
-
- .. change::
- :tags: mysql
- :tickets:
-
- The sqltypes.Binary passthrough now always builds a BLOB,
- avoiding problems with very old database versions
-
- .. change::
- :tags: mysql
- :tickets:
-
- support for column-level CHARACTER SET and COLLATE declarations,
- as well as ASCII, UNICODE, NATIONAL and BINARY shorthand.
-
- .. change::
- :tags: firebird
- :tickets:
-
- set max identifier length to 31
-
- .. change::
- :tags: firebird
- :tickets:
-
- supports_sane_rowcount() set to False due to ticket #370.
- versioned_id_col feature won't work in FB.
-
- .. change::
- :tags: firebird
- :tickets:
-
- some execution fixes
-
- .. change::
- :tags: firebird
- :tickets:
-
- new association proxy implementation, implementing complete
- proxies to list, dict and set-based relation collections
-
- .. change::
- :tags: firebird
- :tickets:
-
- added orderinglist, a custom list class that synchronizes an
- object attribute with that object's position in the list
-
- .. change::
- :tags: firebird
- :tickets:
-
- small fix to SelectResultsExt to not bypass itself during
- select().
-
- .. change::
- :tags: firebird
- :tickets:
-
- added filter(), filter_by() to assignmapper
-
-.. changelog::
- :version: 0.3.7
- :released: Sun Apr 29 2007
-
- .. change::
- :tags: engines
- :tickets:
-
- warnings module used for issuing warnings (instead of logging)
-
- .. change::
- :tags: engines
- :tickets: 480
-
- cleanup of DBAPI import strategies across all engines
-
- .. change::
- :tags: engines
- :tickets:
-
- refactoring of engine internals which reduces complexity,
- number of codepaths; places more state inside of ExecutionContext
- to allow more dialect control of cursor handling, result sets.
- ResultProxy totally refactored and also has two versions of
- "buffered" result sets used for different purposes.
-
- .. change::
- :tags: engines
- :tickets: 514
-
- server side cursor support fully functional in postgres.
-
- .. change::
- :tags: engines
- :tickets:
-
- improved framework for auto-invalidation of connections that have
- lost their underlying database, via dialect-specific detection
- of exceptions corresponding to that database's disconnect
- related error messages. Additionally, when a "connection no
- longer open" condition is detected, the entire connection pool
- is discarded and replaced with a new instance. #516
-
- .. change::
- :tags: engines
- :tickets: 521
-
- the dialects within sqlalchemy.databases become a setuptools
- entry points. loading the built-in database dialects works the
- same as always, but if none found will fall back to trying
- pkg_resources to load an external module
-
- .. change::
- :tags: engines
- :tickets:
-
- Engine contains a "url" attribute referencing the url.URL object
- used by create_engine().
-
- .. change::
- :tags: sql
- :tickets:
-
- keys() of result set columns are not lowercased, come back
- exactly as they're expressed in cursor.description. note this
- causes colnames to be all caps in oracle.
-
- .. change::
- :tags: sql
- :tickets:
-
- preliminary support for unicode table names, column names and
- SQL statements added, for databases which can support them.
- Works with sqlite and postgres so far. MySQL *mostly* works
- except the has_table() function does not work. Reflection
- works too.
-
- .. change::
- :tags: sql
- :tickets: 522
-
- the Unicode type is now a direct subclass of String, which now
- contains all the "convert_unicode" logic. This helps the variety
- of unicode situations that occur in db's such as MS-SQL to be
- better handled and allows subclassing of the Unicode datatype.
-
- .. change::
- :tags: sql
- :tickets:
-
- ClauseElements can be used in in_() clauses now, such as bind
- parameters, etc. #476
-
- .. change::
- :tags: sql
- :tickets:
-
- reverse operators implemented for `CompareMixin` elements,
- allows expressions like "5 + somecolumn" etc. #474
-
- .. change::
- :tags: sql
- :tickets:
-
- the "where" criterion of an update() and delete() now correlates
- embedded select() statements against the table being updated or
- deleted. this works the same as nested select() statement
- correlation, and can be disabled via the correlate=False flag on
- the embedded select().
-
- .. change::
- :tags: sql
- :tickets: 512
-
- column labels are now generated in the compilation phase, which
- means their lengths are dialect-dependent. So on oracle a label
- that gets truncated to 30 chars will go out to 63 characters
- on postgres. Also, the true labelname is always attached as the
- accessor on the parent Selectable so there's no need to be aware
- of the "truncated" label names.
-
- .. change::
- :tags: sql
- :tickets:
-
- column label and bind param "truncation" also generate
- deterministic names now, based on their ordering within the
- full statement being compiled. this means the same statement
- will produce the same string across application restarts and
- allowing DB query plan caching to work better.
-
- .. change::
- :tags: sql
- :tickets: 513
-
- the "mini" column labels generated when using subqueries, which
- are to work around glitchy SQLite behavior that doesn't understand
- "foo.id" as equivalent to "id", are now only generated in the case
- that those named columns are selected from (part of)
-
- .. change::
- :tags: sql
- :tickets:
-
- the label() method on ColumnElement will properly propagate the
- TypeEngine of the base element out to the label, including a label()
- created from a scalar=True select() statement.
-
- .. change::
- :tags: sql
- :tickets: 513
-
- MS-SQL better detects when a query is a subquery and knows not to
- generate ORDER BY phrases for those
-
- .. change::
- :tags: sql
- :tickets: 505
-
- fix for fetchmany() "size" argument being positional in most
- dbapis
-
- .. change::
- :tags: sql
- :tickets:
-
- sending None as an argument to func.<something> will produce
- an argument of NULL
-
- .. change::
- :tags: sql
- :tickets:
-
- query strings in unicode URLs get keys encoded to ascii
- for \**kwargs compat
-
- .. change::
- :tags: sql
- :tickets: 523
-
- slight tweak to raw execute() change to also support tuples
- for positional parameters, not just lists
-
- .. change::
- :tags: sql
- :tickets:
-
- fix to case() construct to propagate the type of the first
- WHEN condition as the return type of the case statement
-
- .. change::
- :tags: orm
- :tickets:
-
- fixed critical issue when, after options(eagerload()) is used,
- the mapper would then always apply query "wrapping" behavior
- for all subsequent LIMIT/OFFSET/DISTINCT queries, even if no
- eager loading was applied on those subsequent queries.
-
- .. change::
- :tags: orm
- :tickets: 541
-
- added query.with_parent(someinstance) method. searches for
- target instance using lazy join criterion from parent instance.
- takes optional string "property" to isolate the desired relation.
- also adds static Query.query_from_parent(instance, property)
- version.
-
- .. change::
- :tags: orm
- :tickets: 554
-
- improved query.XXX_by(someprop=someinstance) querying to use
- similar methodology to with_parent, i.e. using the "lazy" clause
- which prevents adding the remote instance's table to the SQL,
- thereby making more complex conditions possible
-
- .. change::
- :tags: orm
- :tickets:
-
- added generative versions of aggregates, i.e. sum(), avg(), etc.
- to query. used via query.apply_max(), apply_sum(), etc.
- #552
-
- .. change::
- :tags: orm
- :tickets:
-
- fix to using distinct() or distinct=True in combination with
- join() and similar
-
- .. change::
- :tags: orm
- :tickets:
-
- corresponding to label/bindparam name generation, eager loaders
- generate deterministic names for the aliases they create using
- md5 hashes.
-
- .. change::
- :tags: orm
- :tickets:
-
- improved/fixed custom collection classes when giving it "set"/
- "sets.Set" classes or subclasses (was still looking for append()
- methods on them during lazy loads)
-
- .. change::
- :tags: orm
- :tickets:
-
- restored old "column_property()" ORM function (used to be called
- "column()") to force any column expression to be added as a property
- on a mapper, particularly those that aren't present in the mapped
- selectable. this allows "scalar expressions" of any kind to be
- added as relations (though they have issues with eager loads).
-
- .. change::
- :tags: orm
- :tickets: 533
-
- fix to many-to-many relationships targeting polymorphic mappers
-
- .. change::
- :tags: orm
- :tickets: 543
-
- making progress with session.merge() as well as combining its
- usage with entity_name
-
- .. change::
- :tags: orm
- :tickets:
-
- the usual adjustments to relationships between inheriting mappers,
- in this case establishing relation()s to subclass mappers where
- the join conditions come from the superclass' table
-
- .. change::
- :tags: informix
- :tickets:
-
- informix support added ! courtesy James Zhang, who put a ton
- of effort in.
-
- .. change::
- :tags: sqlite
- :tickets:
-
- removed silly behavior where sqlite would reflect UNIQUE indexes
- as part of the primary key (?!)
-
- .. change::
- :tags: oracle
- :tickets:
-
- small fix to allow successive compiles of the same SELECT object
- which features LIMIT/OFFSET. oracle dialect needs to modify
- the object to have ROW_NUMBER OVER and wasn't performing
- the full series of steps on successive compiles.
-
- .. change::
- :tags: mysql
- :tickets:
-
- support for SSL arguments given as inline within URL query string,
- prefixed with "ssl\_", courtesy terjeros@gmail.com.
-
- .. change::
- :tags: <schemaname>, mysql
- :tickets:
-
- mysql uses "DESCRIBE.<tablename>", catching exceptions
- if table doesn't exist, in order to determine if a table exists.
- this supports unicode table names as well as schema names. tested
- with MySQL5 but should work with 4.1 series as well. (#557)
-
- .. change::
- :tags: extensions
- :tickets:
-
- big fix to AssociationProxy so that multiple AssociationProxy
- objects can be associated with a single association collection.
-
- .. change::
- :tags: extensions
- :tickets:
-
- assign_mapper names methods according to their keys (i.e. __name__)
- #551
-
- .. change::
- :tags: mssql
- :tickets:
-
- pyodbc is now the preferred DB-API for MSSQL, and if no module is
- specifically requested, will be loaded first on a module probe.
-
- .. change::
- :tags: mssql
- :tickets:
-
- The @@SCOPE_IDENTITY is now used instead of @@IDENTITY. This
- behavior may be overridden with the engine_connect
- "use_scope_identity" keyword parameter, which may also be specified
- in the dburi.
-
-.. changelog::
- :version: 0.3.6
- :released: Fri Mar 23 2007
-
- .. change::
- :tags: sql
- :tickets:
-
- bindparam() names are now repeatable! specify two
- distinct bindparam()s with the same name in a single statement,
- and the key will be shared. proper positional/named args translate
- at compile time. for the old behavior of "aliasing" bind parameters
- with conflicting names, specify "unique=True" - this option is
- still used internally for all the auto-generated (value-based)
- bind parameters.
-
- .. change::
- :tags: sql
- :tickets:
-
- slightly better support for bind params as column clauses, either
- via bindparam() or via literal(), i.e. select([literal('foo')])
-
- .. change::
- :tags: sql
- :tickets:
-
- MetaData can bind to an engine either via "url" or "engine" kwargs
- to constructor, or by using connect() method. BoundMetaData is
- identical to MetaData except engine_or_url param is required.
- DynamicMetaData is the same and provides thread-local connections be
- default.
-
- .. change::
- :tags: sql
- :tickets:
-
- exists() becomes usable as a standalone selectable, not just in a
- WHERE clause, i.e. exists([columns], criterion).select()
-
- .. change::
- :tags: sql
- :tickets:
-
- correlated subqueries work inside of ORDER BY, GROUP BY
-
- .. change::
- :tags: sql
- :tickets:
-
- fixed function execution with explicit connections, i.e.
- conn.execute(func.dosomething())
-
- .. change::
- :tags: sql
- :tickets:
-
- use_labels flag on select() won't auto-create labels for literal text
- column elements, since we can make no assumptions about the text. to
- create labels for literal columns, you can say "somecol AS
- somelabel", or use literal_column("somecol").label("somelabel")
-
- .. change::
- :tags: sql
- :tickets:
-
- quoting won't occur for literal columns when they are "proxied" into
- the column collection for their selectable (is_literal flag is
- propagated). literal columns are specified via
- literal_column("somestring").
-
- .. change::
- :tags: sql
- :tickets:
-
- added "fold_equivalents" boolean argument to Join.select(), which
- removes 'duplicate' columns from the resulting column clause that
- are known to be equivalent based on the join condition. this is of
- great usage when constructing subqueries of joins which Postgres
- complains about if duplicate column names are present.
-
- .. change::
- :tags: sql
- :tickets: 503
-
- fixed use_alter flag on ForeignKeyConstraint
-
- .. change::
- :tags: sql
- :tickets: 506
-
- fixed usage of 2.4-only "reversed" in topological.py
-
- .. change::
- :tags: sql
- :tickets: 501
-
- for hackers, refactored the "visitor" system of ClauseElement and
- SchemaItem so that the traversal of items is controlled by the
- ClauseVisitor itself, using the method visitor.traverse(item).
- accept_visitor() methods can still be called directly but will not
- do any traversal of child items. ClauseElement/SchemaItem now have a
- configurable get_children() method to return the collection of child
- elements for each parent object. This allows the full traversal of
- items to be clear and unambiguous (as well as loggable), with an
- easy method of limiting a traversal (just pass flags which are
- picked up by appropriate get_children() methods).
-
- .. change::
- :tags: sql
- :tickets:
-
- the "else\_" parameter to the case statement now properly works when
- set to zero.
-
- .. change::
- :tags: orm
- :tickets:
-
- the full featureset of the SelectResults extension has been merged
- into a new set of methods available off of Query. These methods
- all provide "generative" behavior, whereby the Query is copied
- and a new one returned with additional criterion added.
- The new methods include:
-
- * filter() - applies select criterion to the query
- * filter_by() - applies "by"-style criterion to the query
- * avg() - return the avg() function on the given column
- * join() - join to a property (or across a list of properties)
- * outerjoin() - like join() but uses LEFT OUTER JOIN
- * limit()/offset() - apply LIMIT/OFFSET range-based access
- which applies limit/offset: session.query(Foo)[3:5]
- * distinct() - apply DISTINCT
- * list() - evaluate the criterion and return results
-
- no incompatible changes have been made to Query's API and no methods
- have been deprecated. Existing methods like select(), select_by(),
- get(), get_by() all execute the query at once and return results
- like they always did. join_to()/join_via() are still there although
- the generative join()/outerjoin() methods are easier to use.
-
- .. change::
- :tags: orm
- :tickets:
-
- the return value for multiple mappers used with instances() now
- returns a cartesian product of the requested list of mappers,
- represented as a list of tuples. this corresponds to the documented
- behavior. So that instances match up properly, the "uniquing" is
- disabled when this feature is used.
-
- .. change::
- :tags: orm
- :tickets:
-
- Query has add_entity() and add_column() generative methods. these
- will add the given mapper/class or ColumnElement to the query at
- compile time, and apply them to the instances() method. the user is
- responsible for constructing reasonable join conditions (otherwise
- you can get full cartesian products). result set is the list of
- tuples, non-uniqued.
-
- .. change::
- :tags: orm
- :tickets:
-
- strings and columns can also be sent to the \*args of instances()
- where those exact result columns will be part of the result tuples.
-
- .. change::
- :tags: orm
- :tickets:
-
- a full select() construct can be passed to query.select() (which
- worked anyway), but also query.selectfirst(), query.selectone()
- which will be used as is (i.e. no query is compiled). works
- similarly to sending the results to instances().
-
- .. change::
- :tags: orm
- :tickets: 495
-
- eager loading will not "aliasize" "order by" clauses that were
- placed in the select statement by something other than the eager
- loader itself, to fix possibility of dupe columns as illustrated in. however, this means you have to be more careful with
- the columns placed in the "order by" of Query.select(), that you
- have explicitly named them in your criterion (i.e. you can't rely on
- the eager loader adding them in for you)
-
- .. change::
- :tags: orm
- :tickets:
-
- added a handy multi-use "identity_key()" method to Session, allowing
- the generation of identity keys for primary key values, instances,
- and rows, courtesy Daniel Miller
-
- .. change::
- :tags: orm
- :tickets: 249
-
- many-to-many table will be properly handled even for operations that
- occur on the "backref" side of the operation
-
- .. change::
- :tags: orm
- :tickets: 492
-
- added "refresh-expire" cascade. allows refresh() and
- expire() calls to propagate along relationships.
-
- .. change::
- :tags: orm
- :tickets: 493
-
- more fixes to polymorphic relations, involving proper lazy-clause
- generation on many-to-one relationships to polymorphic mappers. also fixes to detection of "direction", more specific
- targeting of columns that belong to the polymorphic union vs. those
- that don't.
-
- .. change::
- :tags: orm
- :tickets:
-
- some fixes to relationship calcs when using "viewonly=True" to pull
- in other tables into the join condition which aren't parent of the
- relationship's parent/child mappings
-
- .. change::
- :tags: orm
- :tickets:
-
- flush fixes on cyclical-referential relationships that contain
- references to other instances outside of the cyclical chain, when
- some of the objects in the cycle are not actually part of the flush
-
- .. change::
- :tags: orm
- :tickets: 500
-
- put an aggressive check for "flushing object A with a collection of
- B's, but you put a C in the collection" error condition - **even if
- C is a subclass of B**, unless B's mapper loads polymorphically.
- Otherwise, the collection will later load a "B" which should be a
- "C" (since its not polymorphic) which breaks in bi-directional
- relationships (i.e. C has its A, but A's backref will lazyload it as
- a different instance of type "B") This check is going
- to bite some of you who do this without issues, so the error message
- will also document a flag "enable_typechecks=False" to disable this
- checking. But be aware that bi-directional relationships in
- particular become fragile without this check.
-
- .. change::
- :tags: extensions
- :tickets: 472
-
- options() method on SelectResults now implemented "generatively"
- like the rest of the SelectResults methods. But
- you're going to just use Query now anyway.
-
- .. change::
- :tags: extensions
- :tickets:
-
- query() method is added by assignmapper. this helps with
- navigating to all the new generative methods on Query.
-
- .. change::
- :tags: ms-sql
- :tickets:
-
- removed seconds input on DATE column types (probably
- should remove the time altogether)
-
- .. change::
- :tags: ms-sql
- :tickets:
-
- null values in float fields no longer raise errors
-
- .. change::
- :tags: ms-sql
- :tickets:
-
- LIMIT with OFFSET now raises an error (MS-SQL has no OFFSET support)
-
- .. change::
- :tags: ms-sql
- :tickets: 509
-
- added an facility to use the MSSQL type VARCHAR(max) instead of TEXT
- for large unsized string fields. Use the new "text_as_varchar" to
- turn it on.
-
- .. change::
- :tags: ms-sql
- :tickets:
-
- ORDER BY clauses without a LIMIT are now stripped in subqueries, as
- MS-SQL forbids this usage
-
- .. change::
- :tags: ms-sql
- :tickets: 480
-
- cleanup of module importing code; specifiable DB-API module; more
- explicit ordering of module preferences.
-
- .. change::
- :tags: oracle
- :tickets:
-
- got binary working for any size input ! cx_oracle works fine,
- it was my fault as BINARY was being passed and not BLOB for
- setinputsizes (also unit tests weren't even setting input sizes).
-
- .. change::
- :tags: oracle
- :tickets:
-
- also fixed CLOB read/write on a separate changeset.
-
- .. change::
- :tags: oracle
- :tickets:
-
- auto_setinputsizes defaults to True for Oracle, fixed cases where
- it improperly propagated bad types.
-
- .. change::
- :tags: mysql
- :tickets:
-
- added a catchall \**kwargs to MSString, to help reflection of
- obscure types (like "varchar() binary" in MS 4.0)
-
- .. change::
- :tags: mysql
- :tickets:
-
- added explicit MSTimeStamp type which takes effect when using
- types.TIMESTAMP.
-
-.. changelog::
- :version: 0.3.5
- :released: Thu Feb 22 2007
-
- .. change::
- :tags: sql
- :tickets:
-
- the value of "case_sensitive" defaults to True now, regardless of the
- casing of the identifier, unless specifically set to False. this is
- because the object might be label'ed as something else which does
- contain mixed case, and propagating "case_sensitive=False" breaks that.
- Other fixes to quoting when using labels and "fake" column objects
-
- .. change::
- :tags: sql
- :tickets:
-
- added a "supports_execution()" method to ClauseElement, so that
- individual kinds of clauses can express if they are appropriate for
- executing...such as, you can execute a "select", but not a "Table" or a
- "Join".
-
- .. change::
- :tags: sql
- :tickets:
-
- fixed argument passing to straight textual execute() on engine,
- connection. can handle \*args or a list instance for positional, \**kwargs
- or a dict instance for named args, or a list of list or dicts to invoke
- executemany()
-
- .. change::
- :tags: sql
- :tickets:
-
- small fix to BoundMetaData to accept unicode or string URLs
-
- .. change::
- :tags: sql
- :tickets: 466
-
- fixed named PrimaryKeyConstraint generation courtesy
- andrija at gmail
-
- .. change::
- :tags: sql
- :tickets: 464
-
- fixed generation of CHECK constraints on columns
-
- .. change::
- :tags: sql
- :tickets:
-
- fixes to tometadata() operation to propagate Constraints at column and
- table level
-
- .. change::
- :tags: oracle
- :tickets: 436
-
- when returning "rowid" as the ORDER BY column or in use with ROW_NUMBER
- OVER, oracle dialect checks the selectable its being applied to and will
- switch to table PK if not applicable, i.e. for a UNION. checking for
- DISTINCT, GROUP BY (other places that rowid is invalid) still a TODO.
- allows polymorphic mappings to function.
-
- .. change::
- :tags: oracle
- :tickets:
-
- sequences on a non-pk column will properly fire off on INSERT
-
- .. change::
- :tags: oracle
- :tickets: 435
-
- added PrefetchingResultProxy support to pre-fetch LOB columns when they
- are known to be present, fixes
-
- .. change::
- :tags: oracle
- :tickets: 379
-
- implemented reflection of tables based on synonyms, including across
- dblinks
-
- .. change::
- :tags: oracle
- :tickets: 363
-
- issues a log warning when a related table can't be reflected due to
- certain permission errors
-
- .. change::
- :tags: mysql
- :tickets:
-
- fix to reflection on older DB's that might return array() type for
- "show variables like" statements
-
- .. change::
- :tags: postgres
- :tickets: 442
-
- better reflection of sequences for alternate-schema Tables
-
- .. change::
- :tags: postgres
- :tickets:
-
- sequences on a non-pk column will properly fire off on INSERT
-
- .. change::
- :tags: postgres
- :tickets: 460, 444
-
- added PGInterval type, PGInet type
-
- .. change::
- :tags: mssql
- :tickets: 419
-
- preliminary support for pyodbc (Yay!)
-
- .. change::
- :tags: mssql
- :tickets: 298
-
- better support for NVARCHAR types added
-
- .. change::
- :tags: mssql
- :tickets:
-
- fix for commit logic on pymssql
-
- .. change::
- :tags: mssql
- :tickets: 456
-
- fix for query.get() with schema
-
- .. change::
- :tags: mssql
- :tickets: 473
-
- fix for non-integer relationships
-
- .. change::
- :tags: mssql
- :tickets: 419
-
- DB-API module now selectable at run-time
-
- .. change::
- :tags: tickets:422, 481, 415, mssql
- :tickets:
-
- now passes many more unit tests
-
- .. change::
- :tags: mssql
- :tickets: 479
-
- better unittest compatibility with ANSI functions
-
- .. change::
- :tags: mssql
- :tickets: 415
-
- improved support for implicit sequence PK columns with auto-insert
-
- .. change::
- :tags: mssql
- :tickets: 371
-
- fix for blank password in adodbapi
-
- .. change::
- :tags: mssql
- :tickets: 481
-
- fixes to get unit tests working with pyodbc
-
- .. change::
- :tags: mssql
- :tickets:
-
- fix to auto_identity_insert on db-url query
-
- .. change::
- :tags: mssql
- :tickets:
-
- added query_timeout to db-url query params. currently works only for
- pymssql
-
- .. change::
- :tags: mssql
- :tickets:
-
- tested with pymssql 0.8.0 (which is now LGPL)
-
- .. change::
- :tags: orm, bugs
- :tickets: 441, 448, 439
-
- another refactoring to relationship calculation. Allows more accurate
- ORM behavior with relationships from/to/between mappers, particularly
- polymorphic mappers, also their usage with Query, SelectResults. tickets
- include,,.
-
- .. change::
- :tags: orm, bugs
- :tickets:
-
- removed deprecated method of specifying custom collections on classes;
- you must now use the "collection_class" option. the old way was
- beginning to produce conflicts when people used assign_mapper(), which
- now patches an "options" method, in conjunction with a relationship
- named "options". (relationships take precedence over monkeypatched
- assign_mapper methods).
-
- .. change::
- :tags: orm, bugs
- :tickets: 454
-
- extension() query option propagates to Mapper._instance() method so that
- all loading-related methods get called
-
- .. change::
- :tags: orm, bugs
- :tickets:
-
- eager relation to an inheriting mapper won't fail if no rows returned for
- the relationship.
-
- .. change::
- :tags: orm, bugs
- :tickets: 486
-
- eager relation loading bug fixed for eager relation on multiple
- descendant classes
-
- .. change::
- :tags: orm, bugs
- :tickets: 423
-
- fix for very large topological sorts, courtesy ants.aasma at gmail
-
- .. change::
- :tags: orm, bugs
- :tickets:
-
- eager loading is slightly more strict about detecting "self-referential"
- relationships, specifically between polymorphic mappers. this results in
- an "eager degrade" to lazy loading.
-
- .. change::
- :tags: orm, bugs
- :tickets: 449
-
- improved support for complex queries embedded into "where" criterion for
- query.select()
-
- .. change::
- :tags: orm, bugs
- :tickets: 485
-
- mapper options like eagerload(), lazyload(), deferred(), will work for
- "synonym()" relationships
-
- .. change::
- :tags: orm, bugs
- :tickets: 445
-
- fixed bug where cascade operations incorrectly included deleted
- collection items in the cascade
-
- .. change::
- :tags: orm, bugs
- :tickets: 478
-
- fixed relationship deletion error when one-to-many child item is moved
- to a new parent in a single unit of work
-
- .. change::
- :tags: orm, bugs
- :tickets:
-
- fixed relationship deletion error where parent/child with a single
- column as PK/FK on the child would raise a "blank out the primary key"
- error, if manually deleted or "delete" cascade without "delete-orphan"
- was used
-
- .. change::
- :tags: orm, bugs
- :tickets:
-
- fix to deferred so that load operation doesn't mistakenly occur when only
- PK col attributes are set
-
- .. change::
- :tags: orm, enhancements
- :tickets: 385
-
- implemented foreign_keys argument to mapper. use in
- conjunction with primaryjoin/secondaryjoin arguments to specify/override
- foreign keys defined on the Table instance.
-
- .. change::
- :tags: orm, enhancements
- :tickets:
-
- contains_eager('foo') automatically implies eagerload('foo')
-
- .. change::
- :tags: orm, enhancements
- :tickets:
-
- added "alias" argument to contains_eager(). use it to specify the string
- name or Alias instance of an alias used in the query for the eagerly
- loaded child items. easier to use than "decorator"
-
- .. change::
- :tags: orm, enhancements
- :tickets:
-
- added "contains_alias()" option for result set mapping to an alias of
- the mapped table
-
- .. change::
- :tags: orm, enhancements
- :tickets: 468
-
- added support for py2.5 "with" statement with SessionTransaction
-
- .. change::
- :tags: extensions
- :tickets:
-
- added distinct() method to SelectResults. generally should only make a
- difference when using count().
-
- .. change::
- :tags: extensions
- :tickets: 472
-
- added options() method to SelectResults, equivalent to query.options()
-
- .. change::
- :tags: extensions
- :tickets: 462
-
- added optional __table_opts__ dictionary to ActiveMapper, will send kw
- options to Table objects
-
- .. change::
- :tags: extensions
- :tickets: 467
-
- added selectfirst(), selectfirst_by() to assign_mapper
-
-.. changelog::
- :version: 0.3.4
- :released: Tue Jan 23 2007
-
- .. change::
- :tags: general
- :tickets:
-
- global "insure"->"ensure" change. in US english "insure" is actually
- largely interchangeable with "ensure" (so says the dictionary), so I'm not
- completely illiterate, but its definitely sub-optimal to "ensure" which is
- non-ambiguous.
-
- .. change::
- :tags: sql
- :tickets:
-
- added "fetchmany()" support to ResultProxy
-
- .. change::
- :tags: sql
- :tickets:
-
- added support for column "key" attribute to be usable in
- row[<key>]/row.<key>
-
- .. change::
- :tags: sql
- :tickets:
-
- changed "BooleanExpression" to subclass from "BinaryExpression", so that
- boolean expressions can also follow column-clause behaviors (i.e. label(),
- etc).
-
- .. change::
- :tags: sql
- :tickets:
-
- trailing underscores are trimmed from func.<xxx> calls, such as func.if_()
-
- .. change::
- :tags: sql
- :tickets:
-
- fix to correlation of subqueries when the column list of the select
- statement is constructed with individual calls to append_column(); this
- fixes an ORM bug whereby nested select statements were not getting
- correlated with the main select generated by the Query object.
-
- .. change::
- :tags: sql
- :tickets:
-
- another fix to subquery correlation so that a subquery which has only one
- FROM element will *not* correlate that single element, since at least one
- FROM element is required in a query.
-
- .. change::
- :tags: sql
- :tickets: 414
-
- default "timezone" setting is now False. this corresponds to Python's
- datetime behavior as well as Postgres' timestamp/time types (which is the
- only timezone-sensitive dialect at the moment)
-
- .. change::
- :tags: sql
- :tickets:
-
- the "op()" function is now treated as an "operation", rather than a
- "comparison". the difference is, an operation produces a BinaryExpression
- from which further operations can occur whereas comparison produces the
- more restrictive BooleanExpression
-
- .. change::
- :tags: sql
- :tickets:
-
- trying to redefine a reflected primary key column as non-primary key raises
- an error
-
- .. change::
- :tags: sql
- :tickets:
-
- type system slightly modified to support TypeDecorators that can be
- overridden by the dialect (ok, that's not very clear, it allows the mssql
- tweak below to be possible)
-
- .. change::
- :tags: mssql
- :tickets:
-
- added an NVarchar type (produces NVARCHAR), also MSUnicode which provides
- Unicode-translation for the NVarchar regardless of dialect convert_unicode
- setting.
-
- .. change::
- :tags: postgres
- :tickets: 424
-
- fix to the initial checkfirst for tables to take current schema into
- account
-
- .. change::
- :tags: postgres
- :tickets:
-
- postgres has an optional "server_side_cursors=True" flag which will utilize
- server side cursors. these are appropriate for fetching only partial
- results and are necessary for working with very large unbounded result
- sets. While we'd like this to be the default behavior, different
- environments seem to have different results and the causes have not been
- isolated so we are leaving the feature off by default for now. Uses an
- apparently undocumented psycopg2 behavior recently discovered on the
- psycopg mailing list.
-
- .. change::
- :tags: postgres
- :tickets:
-
- added "BIGSERIAL" support for postgres table with
- PGBigInteger/autoincrement
-
- .. change::
- :tags: postgres
- :tickets: 402
-
- fixes to postgres reflection to better handle when schema names are
- present; thanks to jason (at) ncsmags.com
-
- .. change::
- :tags: mysql
- :tickets: 420
-
- mysql is inconsistent with what kinds of quotes it uses in foreign keys
- during a SHOW CREATE TABLE, reflection updated to accommodate for all three
- styles
-
- .. change::
- :tags: mysql
- :tickets: 418
-
- mysql table create options work on a generic passthru now, i.e. Table(...,
- mysql_engine='InnoDB', mysql_collate="latin1_german2_ci",
- mysql_auto_increment="5", mysql_<somearg>...), helps
-
- .. change::
- :tags: firebird
- :tickets: 408
-
- order of constraint creation puts primary key first before all other
- constraints; required for firebird, not a bad idea for others
-
- .. change::
- :tags: firebird
- :tickets: 409
-
- Firebird fix to autoload multifield foreign keys
-
- .. change::
- :tags: firebird
- :tickets: 409
-
- Firebird NUMERIC type properly handles a type without precision
-
- .. change::
- :tags: oracle
- :tickets:
-
- *slight* support for binary, but still need to figure out how to insert
- reasonably large values (over 4K). requires auto_setinputsizes=True sent to
- create_engine(), rows must be fully fetched individually, etc.
-
- .. change::
- :tags: orm
- :tickets:
-
- poked the first hole in the can of worms: saying
- query.select_by(somerelationname=someinstance) will create the join of the
- primary key columns represented by "somerelationname"'s mapper to the
- actual primary key in "someinstance".
-
- .. change::
- :tags: orm
- :tickets:
-
- reworked how relations interact with "polymorphic" mappers, i.e. mappers
- that have a select_table as well as polymorphic flags. better determination
- of proper join conditions, interaction with user- defined join conditions,
- and support for self-referential polymorphic mappers.
-
- .. change::
- :tags: orm
- :tickets:
-
- related to polymorphic mapping relations, some deeper error checking when
- compiling relations, to detect an ambiguous "primaryjoin" in the case that
- both sides of the relationship have foreign key references in the primary
- join condition. also tightened down conditions used to locate "relation
- direction", associating the "foreignkey" of the relationship with the
- "primaryjoin"
-
- .. change::
- :tags: orm
- :tickets:
-
- a little bit of improvement to the concept of a "concrete" inheritance
- mapping, though that concept is not well fleshed out yet (added test case
- to support concrete mappers on top of a polymorphic base).
-
- .. change::
- :tags: orm
- :tickets:
-
- fix to "proxy=True" behavior on synonym()
-
- .. change::
- :tags: orm
- :tickets: 427
-
- fixed bug where delete-orphan basically didn't work with many-to-many
- relationships, backref presence generally hid the symptom
-
- .. change::
- :tags: orm
- :tickets:
-
- added a mutex to the mapper compilation step. ive been reluctant to add any
- kind of threading anything to SA but this is one spot that its really
- needed since mappers are typically "global", and while their state does not
- change during normal operation, the initial compilation step does modify
- internal state significantly, and this step usually occurs not at
- module-level initialization time (unless you call compile()) but at
- first-request time
-
- .. change::
- :tags: orm
- :tickets:
-
- basic idea of "session.merge()" actually implemented. needs more testing.
-
- .. change::
- :tags: orm
- :tickets:
-
- added "compile_mappers()" function as a shortcut to compiling all mappers
-
- .. change::
- :tags: orm
- :tickets:
-
- fix to MapperExtension create_instance so that entity_name properly
- associated with new instance
-
- .. change::
- :tags: orm
- :tickets:
-
- speed enhancements to ORM object instantiation, eager loading of rows
-
- .. change::
- :tags: orm
- :tickets: 406
-
- invalid options sent to 'cascade' string will raise an exception
-
- .. change::
- :tags: orm
- :tickets: 407
-
- fixed bug in mapper refresh/expire whereby eager loaders didn't properly
- re-populate item lists
-
- .. change::
- :tags: orm
- :tickets: 413
-
- fix to post_update to ensure rows are updated even for non insert/delete
- scenarios
-
- .. change::
- :tags: orm
- :tickets: 412
-
- added an error message if you actually try to modify primary key values on
- an entity and then flush it
-
- .. change::
- :tags: extensions
- :tickets: 426
-
- added "validate=False" argument to assign_mapper, if True will ensure that
- only mapped attributes are named
-
- .. change::
- :tags: extensions
- :tickets:
-
- assign_mapper gets "options", "instances" functions added (i.e.
- MyClass.instances())
-
-.. changelog::
- :version: 0.3.3
- :released: Fri Dec 15 2006
-
- .. change::
- :tags:
- :tickets:
-
- string-based FROM clauses fixed, i.e. select(..., from_obj=["sometext"])
-
- .. change::
- :tags:
- :tickets:
-
- fixes to passive_deletes flag, lazy=None (noload) flag
-
- .. change::
- :tags:
- :tickets:
-
- added example/docs for dealing with large collections
-
- .. change::
- :tags:
- :tickets:
-
- added object_session() method to sqlalchemy namespace
-
- .. change::
- :tags:
- :tickets:
-
- fixed QueuePool bug whereby its better able to reconnect to a database
- that was not reachable (thanks to Sébastien Lelong), also fixed dispose()
- method
-
- .. change::
- :tags:
- :tickets: 396
-
- patch that makes MySQL rowcount work correctly!
-
- .. change::
- :tags:
- :tickets:
-
- fix to MySQL catch of 2006/2014 errors to properly re-raise OperationalError
- exception
-
-.. changelog::
- :version: 0.3.2
- :released: Sun Dec 10 2006
-
- .. change::
- :tags:
- :tickets: 387
-
- major connection pool bug fixed. fixes MySQL out of sync
- errors, will also prevent transactions getting rolled back
- accidentally in all DBs
-
- .. change::
- :tags:
- :tickets:
-
- major speed enhancements vs. 0.3.1, to bring speed
- back to 0.2.8 levels
-
- .. change::
- :tags:
- :tickets:
-
- made conditional dozens of debug log calls that were
- time-intensive to generate log messages
-
- .. change::
- :tags:
- :tickets:
-
- fixed bug in cascade rules whereby the entire object graph
- could be unnecessarily cascaded on the save/update cascade
-
- .. change::
- :tags:
- :tickets:
-
- various speedups in attributes module
-
- .. change::
- :tags:
- :tickets: 388
-
- identity map in Session is by default *no longer weak referencing*.
- to have it be weak referencing, use create_session(weak_identity_map=True)
- fixes
-
- .. change::
- :tags:
- :tickets:
-
- MySQL detects errors 2006 (server has gone away) and 2014
- (commands out of sync) and invalidates the connection on which it occurred.
-
- .. change::
- :tags:
- :tickets: 307
-
- MySQL bool type fix:
-
- .. change::
- :tags:
- :tickets: 382, 349
-
- postgres reflection fixes:
-
- .. change::
- :tags:
- :tickets: 247
-
- added keywords for EXCEPT, INTERSECT, EXCEPT ALL, INTERSECT ALL
-
- .. change::
- :tags:
- :tickets: 2110
-
- assign_mapper in assignmapper extension returns the created mapper
-
- .. change::
- :tags:
- :tickets:
-
- added label() function to Select class, when scalar=True is used
- to create a scalar subquery
- i.e. "select x, y, (select max(foo) from table) AS foomax from table"
-
- .. change::
- :tags:
- :tickets:
-
- added onupdate and ondelete keyword arguments to ForeignKey; propagate
- to underlying ForeignKeyConstraint if present. (don't propagate in the
- other direction, however)
-
- .. change::
- :tags:
- :tickets:
-
- fix to session.update() to preserve "dirty" status of incoming object
-
- .. change::
- :tags:
- :tickets:
-
- sending a selectable to an IN via the in_() function no longer creates
- a "union" out of multiple selects; only one selectable to a the in_() function
- is allowed now (make a union yourself if union is needed)
-
- .. change::
- :tags:
- :tickets:
-
- improved support for disabling save-update cascade via cascade="none" etc.
-
- .. change::
- :tags:
- :tickets:
-
- added "remote_side" argument to relation(), used only with self-referential
- mappers to force the direction of the parent/child relationship. replaces
- the usage of the "foreignkey" parameter for "switching" the direction.
- "foreignkey" argument is deprecated for all uses and will eventually
- be replaced by an argument dedicated to ForeignKey specification on mappers.
-
-.. changelog::
- :version: 0.3.1
- :released: Mon Nov 13 2006
-
- .. change::
- :tags: engine/pool
- :tickets:
-
- some new Pool utility classes, updated docs
-
- .. change::
- :tags: engine/pool
- :tickets:
-
- "use_threadlocal" on Pool defaults to False (same as create_engine)
-
- .. change::
- :tags: engine/pool
- :tickets:
-
- fixed direct execution of Compiled objects
-
- .. change::
- :tags: engine/pool
- :tickets:
-
- create_engine() reworked to be strict about incoming \**kwargs. all keyword
- arguments must be consumed by one of the dialect, connection pool, and engine
- constructors, else a TypeError is thrown which describes the full set of
- invalid kwargs in relation to the selected dialect/pool/engine configuration.
-
- .. change::
- :tags: databases/types
- :tickets:
-
- MySQL catches exception on "describe" and reports as NoSuchTableError
-
- .. change::
- :tags: databases/types
- :tickets:
-
- further fixes to sqlite booleans, weren't working as defaults
-
- .. change::
- :tags: databases/types
- :tickets:
-
- fix to postgres sequence quoting when using schemas
-
- .. change::
- :tags: orm
- :tickets:
-
- the "delete" cascade will load in all child objects, if they were not
- loaded already. this can be turned off (i.e. the old behavior) by setting
- passive_deletes=True on a relation().
-
- .. change::
- :tags: orm
- :tickets:
-
- adjustments to reworked eager query generation to not fail on circular
- eager-loaded relationships (like backrefs)
-
- .. change::
- :tags: orm
- :tickets:
-
- fixed bug where eagerload() (nor lazyload()) option didn't properly
- instruct the Query whether or not to use "nesting" when producing a
- LIMIT query.
-
- .. change::
- :tags: orm
- :tickets: 360
-
- fixed bug in circular dependency sorting at flush time; if object A
- contained a cyclical many-to-one relationship to object B, and object B
- was just attached to object A, *but* object B itself wasn't changed,
- the many-to-one synchronize of B's primary key attribute to A's foreign key
- attribute wouldn't occur.
-
- .. change::
- :tags: orm
- :tickets: 325
-
- implemented from_obj argument for query.count, improves count function
- on selectresults
-
- .. change::
- :tags: orm
- :tickets:
-
- added an assertion within the "cascade" step of ORM relationships to check
- that the class of object attached to a parent object is appropriate
- (i.e. if A.items stores B objects, raise an error if a C is appended to A.items)
-
- .. change::
- :tags: orm
- :tickets:
-
- new extension sqlalchemy.ext.associationproxy, provides transparent
- "association object" mappings. new example
- examples/association/proxied_association.py illustrates.
-
- .. change::
- :tags: orm
- :tickets:
-
- improvement to single table inheritance to load full hierarchies beneath
- the target class
-
- .. change::
- :tags: orm
- :tickets: 362
-
- fix to subtle condition in topological sort where a node could appear twice,
- for
-
- .. change::
- :tags: orm
- :tickets: 365
-
- additional rework to topological sort, refactoring, for
-
- .. change::
- :tags: orm
- :tickets:
-
- "delete-orphan" for a certain type can be set on more than one parent class;
- the instance is an "orphan" only if its not attached to *any* of those parents
-
-.. changelog::
- :version: 0.3.0
- :released: Sun Oct 22 2006
-
- .. change::
- :tags: general
- :tickets:
-
- logging is now implemented via standard python "logging" module.
- "echo" keyword parameters are still functional but set/unset
- log levels for their respective classes/instances. all logging
- can be controlled directly through the Python API by setting
- INFO and DEBUG levels for loggers in the "sqlalchemy" namespace.
- class-level logging is under "sqlalchemy.<module>.<classname>",
- instance-level logging under "sqlalchemy.<module>.<classname>.0x..<00-FF>".
- Test suite includes "--log-info" and "--log-debug" arguments
- which work independently of --verbose/--quiet. Logging added
- to orm to allow tracking of mapper configurations, row iteration.
-
- .. change::
- :tags: general
- :tickets:
-
- the documentation-generation system has been overhauled to be
- much simpler in design and more integrated with Markdown
-
- .. change::
- :tags: sqlite
- :tickets:
-
- sqlite boolean datatype converts False/True to 0/1 by default
-
- .. change::
- :tags: sqlite
- :tickets: 335
-
- fixes to Date/Time (SLDate/SLTime) types; works as good as postgres
- now
-
- .. change::
- :tags: ms-sql
- :tickets:
-
- fixes bug 261 (table reflection broken for MS-SQL case-sensitive
- databases)
-
- .. change::
- :tags: ms-sql
- :tickets:
-
- can now specify port for pymssql
-
- .. change::
- :tags: ms-sql
- :tickets:
-
- introduces new "auto_identity_insert" option for auto-switching
- between "SET IDENTITY_INSERT" mode when values specified for IDENTITY columns
-
- .. change::
- :tags: ms-sql
- :tickets:
-
- now supports multi-column foreign keys
-
- .. change::
- :tags: ms-sql
- :tickets:
-
- fix to reflecting date/datetime columns
-
- .. change::
- :tags: ms-sql
- :tickets:
-
- NCHAR and NVARCHAR type support added
-
- .. change::
- :tags: oracle
- :tickets:
-
- Oracle has experimental support for cx_Oracle.TIMESTAMP, which requires
- a setinputsizes() call on the cursor that is now enabled via the
- 'auto_setinputsizes' flag to the oracle dialect.
-
- .. change::
- :tags: firebird
- :tickets:
-
- aliases do not use "AS"
-
- .. change::
- :tags: firebird
- :tickets:
-
- correctly raises NoSuchTableError when reflecting non-existent table
-
- .. change::
- :tags: schema
- :tickets:
-
- a fair amount of cleanup to the schema package, removal of ambiguous
- methods, methods that are no longer needed. slightly more constrained
- usage, greater emphasis on explicitness
-
- .. change::
- :tags: schema
- :tickets:
-
- the "primary_key" attribute of Table and other selectables becomes
- a setlike ColumnCollection object; is ordered but not numerically
- indexed. a comparison clause between two pks that are derived from the
- same underlying tables (i.e. such as two Alias objects) can be generated
- via table1.primary_key==table2.primary_key
-
- .. change::
- :tags: schema
- :tickets:
-
- ForeignKey(Constraint) supports "use_alter=True", to create/drop a foreign key
- via ALTER. this allows circular foreign key relationships to be set up.
-
- .. change::
- :tags: schema
- :tickets:
-
- append_item() methods removed from Table and Column; preferably
- construct Table/Column/related objects inline, but if needed use
- append_column(), append_foreign_key(), append_constraint(), etc.
-
- .. change::
- :tags: schema
- :tickets:
-
- table.create() no longer returns the Table object, instead has no
- return value. the usual case is that tables are created via metadata,
- which is preferable since it will handle table dependencies.
-
- .. change::
- :tags: schema
- :tickets:
-
- added UniqueConstraint (goes at Table level), CheckConstraint
- (goes at Table or Column level).
-
- .. change::
- :tags: schema
- :tickets:
-
- index=False/unique=True on Column now creates a UniqueConstraint,
- index=True/unique=False creates a plain Index,
- index=True/unique=True on Column creates a unique Index. 'index'
- and 'unique' keyword arguments to column are now boolean only; for
- explicit names and groupings of indexes or unique constraints, use the
- UniqueConstraint/Index constructs explicitly.
-
- .. change::
- :tags: schema
- :tickets:
-
- added autoincrement=True to Column; will disable schema generation
- of SERIAL/AUTO_INCREMENT/identity seq for postgres/mysql/mssql if
- explicitly set to False
-
- .. change::
- :tags: schema
- :tickets:
-
- TypeEngine objects now have methods to deal with copying and comparing
- values of their specific type. Currently used by the ORM, see below.
-
- .. change::
- :tags: schema
- :tickets:
-
- fixed condition that occurred during reflection when a primary key
- column was explicitly overridden, where the PrimaryKeyConstraint would
- get both the reflected and the programmatic column doubled up
-
- .. change::
- :tags: schema
- :tickets:
-
- the "foreign_key" attribute on Column and ColumnElement in general
- is deprecated, in favor of the "foreign_keys" list/set-based attribute,
- which takes into account multiple foreign keys on one column.
- "foreign_key" will return the first element in the "foreign_keys" list/set
- or None if the list is empty.
-
- .. change::
- :tags: connections/pooling/execution
- :tickets:
-
- connection pool tracks open cursors and automatically closes them
- if connection is returned to pool with cursors still opened. Can be
- affected by options which cause it to raise an error instead, or to
- do nothing. fixes issues with MySQL, others
-
- .. change::
- :tags: connections/pooling/execution
- :tickets:
-
- fixed bug where Connection wouldn't lose its Transaction
- after commit/rollback
-
- .. change::
- :tags: connections/pooling/execution
- :tickets:
-
- added scalar() method to ComposedSQLEngine, ResultProxy
-
- .. change::
- :tags: connections/pooling/execution
- :tickets:
-
- ResultProxy will close() the underlying cursor when the ResultProxy
- itself is closed. this will auto-close cursors for ResultProxy objects
- that have had all their rows fetched (or had scalar() called).
-
- .. change::
- :tags: connections/pooling/execution
- :tickets:
-
- ResultProxy.fetchall() internally uses DBAPI fetchall() for better efficiency,
- added to mapper iteration as well (courtesy Michael Twomey)
-
- .. change::
- :tags: construction, sql
- :tickets: 292
-
- changed "for_update" parameter to accept False/True/"nowait"
- and "read", the latter two of which are interpreted only by
- Oracle and MySQL
-
- .. change::
- :tags: construction, sql
- :tickets:
-
- added extract() function to sql dialect
- (SELECT extract(field FROM expr))
-
- .. change::
- :tags: construction, sql
- :tickets:
-
- BooleanExpression includes new "negate" argument to specify
- the appropriate negation operator if one is available.
-
- .. change::
- :tags: construction, sql
- :tickets:
-
- calling a negation on an "IN" or "IS" clause will result in
- "NOT IN", "IS NOT" (as opposed to NOT (x IN y)).
-
- .. change::
- :tags: construction, sql
- :tickets: 172
-
- Function objects know what to do in a FROM clause now. their
- behavior should be the same, except now you can also do things like
- select(['*'], from_obj=[func.my_function()]) to get multiple
- columns from the result, or even use sql.column() constructs to name the
- return columns
-
- .. change::
- :tags: orm
- :tickets:
-
- attribute tracking modified to be more intelligent about detecting
- changes, particularly with mutable types. TypeEngine objects now
- take a greater role in defining how to compare two scalar instances,
- including the addition of a MutableType mixin which is implemented by
- PickleType. unit-of-work now tracks the "dirty" list as an expression
- of all persistent objects where the attribute manager detects changes.
- The basic issue that's fixed is detecting changes on PickleType
- objects, but also generalizes type handling and "modified" object
- checking to be more complete and extensible.
-
- .. change::
- :tags: orm
- :tickets:
-
- a wide refactoring to "attribute loader" and "options" architectures.
- ColumnProperty and PropertyLoader define their loading behavior via switchable
- "strategies", and MapperOptions no longer use mapper/property copying
- in order to function; they are instead propagated via QueryContext
- and SelectionContext objects at query/instances time.
- All of the internal copying of mappers and properties that was used to handle
- inheritance as well as options() has been removed; the structure
- of mappers and properties is much simpler than before and is clearly laid out
- in the new 'interfaces' module.
-
- .. change::
- :tags: orm
- :tickets:
-
- related to the mapper/property overhaul, internal refactoring to
- mapper instances() method to use a SelectionContext object to track
- state during the operation.
- SLIGHT API BREAKAGE: the append_result() and populate_instances()
- methods on MapperExtension have a slightly different method signature
- now as a result of the change; hoping that these methods are not
- in widespread use as of yet.
-
- .. change::
- :tags: orm
- :tickets:
-
- instances() method moved to Query now, backwards-compatible
- version remains on Mapper.
-
- .. change::
- :tags: orm
- :tickets:
-
- added contains_eager() MapperOption, used in conjunction with
- instances() to specify properties that should be eagerly loaded
- from the result set, using their plain column names by default, or translated
- given an custom row-translation function.
-
- .. change::
- :tags: orm
- :tickets:
-
- more rearrangements of unit-of-work commit scheme to better allow
- dependencies within circular flushes to work properly...updated
- task traversal/logging implementation
-
- .. change::
- :tags: orm
- :tickets: 321
-
- polymorphic mappers (i.e. using inheritance) now produces INSERT
- statements in order of tables across all inherited classes
-
- .. change::
- :tags: orm
- :tickets:
-
- added an automatic "row switch" feature to mapping, which will
- detect a pending instance/deleted instance pair with the same
- identity key and convert the INSERT/DELETE to a single UPDATE
-
- .. change::
- :tags: orm
- :tickets:
-
- "association" mappings simplified to take advantage of
- automatic "row switch" feature
-
- .. change::
- :tags: orm
- :tickets: 212
-
- "custom list classes" is now implemented via the "collection_class"
- keyword argument to relation(). the old way still works but is
- deprecated
-
- .. change::
- :tags: orm
- :tickets:
-
- added "viewonly" flag to relation(), allows construction of
- relations that have no effect on the flush() process.
-
- .. change::
- :tags: orm
- :tickets: 292
-
- added "lockmode" argument to base Query select/get functions,
- including "with_lockmode" function to get a Query copy that has
- a default locking mode. Will translate "read"/"update"
- arguments into a for_update argument on the select side.
-
- .. change::
- :tags: orm
- :tickets:
-
- implemented "version check" logic in Query/Mapper, used
- when version_id_col is in effect and query.with_lockmode()
- is used to get() an instance that's already loaded
-
- .. change::
- :tags: orm
- :tickets: 208
-
- post_update behavior improved; does a better job at not
- updating too many rows, updates only required columns
-
- .. change::
- :tags: orm
- :tickets: 308
-
- adjustments to eager loading so that its "eager chain" is
- kept separate from the normal mapper setup, thereby
- preventing conflicts with lazy loader operation, fixes
-
- .. change::
- :tags: orm
- :tickets:
-
- fix to deferred group loading
-
- .. change::
- :tags: orm
- :tickets: 346
-
- session.flush() won't close a connection it opened
-
- .. change::
- :tags: orm
- :tickets:
-
- added "batch=True" flag to mapper; if False, save_obj
- will fully save one object at a time including calls
- to before_XXXX and after_XXXX
-
- .. change::
- :tags: orm
- :tickets:
-
- added "column_prefix=None" argument to mapper; prepends the
- given string (typically '_') to column-based attributes automatically
- set up from the mapper's Table
-
- .. change::
- :tags: orm
- :tickets: 315
-
- specifying joins in the from_obj argument of query.select() will
- replace the main table of the query, if the table is somewhere within
- the given from_obj. this makes it possible to produce custom joins and
- outerjoins in queries without the main table getting added twice.
-
- .. change::
- :tags: orm
- :tickets:
-
- eagerloading is adjusted to more thoughtfully attach its LEFT OUTER JOINs
- to the given query, looking for custom "FROM" clauses that may have
- already been set up.
-
- .. change::
- :tags: orm
- :tickets:
-
- added join_to and outerjoin_to transformative methods to SelectResults,
- to build up join/outerjoin conditions based on property names. also
- added select_from to explicitly set from_obj parameter.
-
- .. change::
- :tags: orm
- :tickets:
-
- removed "is_primary" flag from mapper.
+++ /dev/null
-
-=============
-0.4 Changelog
-=============
-
-
-.. changelog::
- :version: 0.4.8
- :released: Sun Oct 12 2008
-
- .. change::
- :tags: orm
- :tickets: 1039
-
- Fixed bug regarding inherit_condition passed
- with "A=B" versus "B=A" leading to errors
-
- .. change::
- :tags: orm
- :tickets:
-
- Changes made to new, dirty and deleted
- collections in
- SessionExtension.before_flush() will take
- effect for that flush.
-
- .. change::
- :tags: orm
- :tickets:
-
- Added label() method to InstrumentedAttribute
- to establish forwards compatibility with 0.5.
-
- .. change::
- :tags: sql
- :tickets: 1074
-
- column.in_(someselect) can now be used as
- a columns-clause expression without the subquery
- bleeding into the FROM clause
-
- .. change::
- :tags: mysql
- :tickets: 1146
-
- Added MSMediumInteger type.
-
- .. change::
- :tags: sqlite
- :tickets: 968
-
- Supplied a custom strftime() function which
- handles dates before 1900.
-
- .. change::
- :tags: sqlite
- :tickets:
-
- String's (and Unicode's, UnicodeText's, etc.)
- convert_unicode logic disabled in the sqlite dialect,
- to adjust for pysqlite 2.5.0's new requirement that
- only Python unicode objects are accepted;
- https://itsystementwicklung.de/pipermail/list-pysqlite/2008-March/000018.html
-
- .. change::
- :tags: oracle
- :tickets: 1155
-
- has_sequence() now takes schema name into account
-
- .. change::
- :tags: oracle
- :tickets: 1121
-
- added BFILE to the list of reflected types
-
-.. changelog::
- :version: 0.4.7p1
- :released: Thu Jul 31 2008
-
- .. change::
- :tags: orm
- :tickets:
-
- Added "add()" and "add_all()" to scoped_session
- methods. Workaround for 0.4.7::
-
- from sqlalchemy.orm.scoping import ScopedSession, instrument
-
- setattr(ScopedSession, "add", instrument("add"))
- setattr(ScopedSession, "add_all", instrument("add_all"))
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed non-2.3 compatible usage of set() and generator
- expression within relation().
-
-.. changelog::
- :version: 0.4.7
- :released: Sat Jul 26 2008
-
- .. change::
- :tags: orm
- :tickets: 1058
-
- The contains() operator when used with many-to-many
- will alias() the secondary (association) table so
- that multiple contains() calls will not conflict
- with each other
-
- .. change::
- :tags: orm
- :tickets:
-
- fixed bug preventing merge() from functioning in
- conjunction with a comparable_property()
-
- .. change::
- :tags: orm
- :tickets:
-
- the enable_typechecks=False setting on relation()
- now only allows subtypes with inheriting mappers.
- Totally unrelated types, or subtypes not set up with
- mapper inheritance against the target mapper are
- still not allowed.
-
- .. change::
- :tags: orm
- :tickets: 976
-
- Added is_active flag to Sessions to detect when
- a transaction is in progress. This
- flag is always True with a "transactional"
- (in 0.5 a non-"autocommit") Session.
-
- .. change::
- :tags: sql
- :tickets:
-
- Fixed bug when calling select([literal('foo')])
- or select([bindparam('foo')]).
-
- .. change::
- :tags: schema
- :tickets: 571
-
- create_all(), drop_all(), create(), drop() all raise
- an error if the table name or schema name contains
- more characters than that dialect's configured
- character limit. Some DB's can handle too-long
- table names during usage, and SQLA can handle this
- as well. But various reflection/
- checkfirst-during-create scenarios fail since we are
- looking for the name within the DB's catalog tables.
-
- .. change::
- :tags: schema
- :tickets: 571, 820
-
- The index name generated when you say "index=True"
- on a Column is truncated to the length appropriate
- for the dialect. Additionally, an Index with a too-
- long name cannot be explicitly dropped with
- Index.drop(), similar to.
-
- .. change::
- :tags: postgres
- :tickets:
-
- Repaired server_side_cursors to properly detect
- text() clauses.
-
- .. change::
- :tags: postgres
- :tickets: 1092
-
- Added PGCidr type.
-
- .. change::
- :tags: mysql
- :tickets:
-
- Added 'CALL' to the list of SQL keywords which return
- result rows.
-
- .. change::
- :tags: oracle
- :tickets:
-
- Oracle get_default_schema_name() "normalizes" the name
- before returning, meaning it returns a lower-case name
- when the identifier is detected as case insensitive.
-
- .. change::
- :tags: oracle
- :tickets: 709
-
- creating/dropping tables takes schema name into account
- when searching for the existing table, so that tables
- in other owner namespaces with the same name do not
- conflict
-
- .. change::
- :tags: oracle
- :tickets: 1062
-
- Cursors now have "arraysize" set to 50 by default on
- them, the value of which is configurable using the
- "arraysize" argument to create_engine() with the
- Oracle dialect. This to account for cx_oracle's default
- setting of "1", which has the effect of many round trips
- being sent to Oracle. This actually works well in
- conjunction with BLOB/CLOB-bound cursors, of which
- there are any number available but only for the life of
- that row request (so BufferedColumnRow is still needed,
- but less so).
-
- .. change::
- :tags: oracle
- :tickets:
-
- sqlite
- - add SLFloat type, which matches the SQLite REAL
- type affinity. Previously, only SLNumeric was provided
- which fulfills NUMERIC affinity, but that's not the
- same as REAL.
-
-.. changelog::
- :version: 0.4.6
- :released: Sat May 10 2008
-
- .. change::
- :tags: orm
- :tickets:
-
- Fix to the recent relation() refactoring which fixes
- exotic viewonly relations which join between local and
- remote table multiple times, with a common column shared
- between the joins.
-
- .. change::
- :tags: orm
- :tickets:
-
- Also re-established viewonly relation() configurations
- that join across multiple tables.
-
- .. change::
- :tags: orm
- :tickets: 610
-
- Added experimental relation() flag to help with
- primaryjoins across functions, etc.,
- _local_remote_pairs=[tuples]. This complements a complex
- primaryjoin condition allowing you to provide the
- individual column pairs which comprise the relation's
- local and remote sides. Also improved lazy load SQL
- generation to handle placing bind params inside of
- functions and other expressions. (partial progress
- towards)
-
- .. change::
- :tags: orm
- :tickets: 1036
-
- repaired single table inheritance such that you
- can single-table inherit from a joined-table inheriting
- mapper without issue.
-
- .. change::
- :tags: orm
- :tickets: 1027
-
- Fixed "concatenate tuple" bug which could occur with
- Query.order_by() if clause adaption had taken place.
-
- .. change::
- :tags: orm
- :tickets:
-
- Removed ancient assertion that mapped selectables require
- "alias names" - the mapper creates its own alias now if
- none is present. Though in this case you need to use the
- class, not the mapped selectable, as the source of column
- attributes - so a warning is still issued.
-
- .. change::
- :tags: orm
- :tickets:
-
- fixes to the "exists" function involving inheritance (any(),
- has(), ~contains()); the full target join will be rendered
- into the EXISTS clause for relations that link to subclasses.
-
- .. change::
- :tags: orm
- :tickets:
-
- restored usage of append_result() extension method for primary
- query rows, when the extension is present and only a single-
- entity result is being returned.
-
- .. change::
- :tags: orm
- :tickets:
-
- Also re-established viewonly relation() configurations that
- join across multiple tables.
-
- .. change::
- :tags: orm
- :tickets:
-
- removed ancient assertion that mapped selectables require
- "alias names" - the mapper creates its own alias now if
- none is present. Though in this case you need to use
- the class, not the mapped selectable, as the source of
- column attributes - so a warning is still issued.
-
- .. change::
- :tags: orm
- :tickets: 1015
-
- refined mapper._save_obj() which was unnecessarily calling
- __ne__() on scalar values during flush
-
- .. change::
- :tags: orm
- :tickets: 1019
-
- added a feature to eager loading whereby subqueries set
- as column_property() with explicit label names (which is not
- necessary, btw) will have the label anonymized when
- the instance is part of the eager join, to prevent
- conflicts with a subquery or column of the same name
- on the parent object.
-
- .. change::
- :tags: orm
- :tickets:
-
- set-based collections \|=, -=, ^= and &= are stricter about
- their operands and only operate on sets, frozensets or
- subclasses of the collection type. Previously, they would
- accept any duck-typed set.
-
- .. change::
- :tags: orm
- :tickets:
-
- added an example dynamic_dict/dynamic_dict.py, illustrating
- a simple way to place dictionary behavior on top of
- a dynamic_loader.
-
- .. change::
- :tags: declarative, extension
- :tickets:
-
- Joined table inheritance mappers use a slightly relaxed
- function to create the "inherit condition" to the parent
- table, so that other foreign keys to not-yet-declared
- Table objects don't trigger an error.
-
- .. change::
- :tags: declarative, extension
- :tickets:
-
- fixed reentrant mapper compile hang when
- a declared attribute is used within ForeignKey,
- ie. ForeignKey(MyOtherClass.someattribute)
-
- .. change::
- :tags: sql
- :tickets:
-
- Added COLLATE support via the .collate(<collation>)
- expression operator and collate(<expr>, <collation>) sql
- function.
-
- .. change::
- :tags: sql
- :tickets:
-
- Fixed bug with union() when applied to non-Table connected
- select statements
-
- .. change::
- :tags: sql
- :tickets: 1014
-
- improved behavior of text() expressions when used as
- FROM clauses, such as select().select_from(text("sometext"))
-
- .. change::
- :tags: sql
- :tickets: 1021
-
- Column.copy() respects the value of "autoincrement",
- fixes usage with Migrate
-
- .. change::
- :tags: engines
- :tickets:
-
- Pool listeners can now be provided as a dictionary of
- callables or a (possibly partial) duck-type of
- PoolListener, your choice.
-
- .. change::
- :tags: engines
- :tickets:
-
- added "rollback_returned" option to Pool which will
- disable the rollback() issued when connections are
- returned. This flag is only safe to use with a database
- which does not support transactions (i.e. MySQL/MyISAM).
-
- .. change::
- :tags: ext
- :tickets:
-
- set-based association proxies \|=, -=, ^= and &= are
- stricter about their operands and only operate on sets,
- frozensets or other association proxies. Previously, they
- would accept any duck-typed set.
-
- .. change::
- :tags: mssql
- :tickets: 1005
-
- Added "odbc_autotranslate" parameter to engine / dburi
- parameters. Any given string will be passed through to the
- ODBC connection string as:
-
- "AutoTranslate=%s" % odbc_autotranslate
-
- .. change::
- :tags: mssql
- :tickets:
-
- Added "odbc_options" parameter to engine / dburi
- parameters. The given string is simply appended to the
- SQLAlchemy-generated odbc connection string.
-
- This should obviate the need of adding a myriad of ODBC
- options in the future.
-
- .. change::
- :tags: firebird
- :tickets:
-
- Handle the "SUBSTRING(:string FROM :start FOR :length)"
- builtin.
-
-.. changelog::
- :version: 0.4.5
- :released: Fri Apr 04 2008
-
- .. change::
- :tags: orm
- :tickets:
-
- A small change in behavior to session.merge() - existing
- objects are checked for based on primary key attributes, not
- necessarily _instance_key. So the widely requested
- capability, that:
-
- x = MyObject(id=1)
- x = sess.merge(x)
-
- will in fact load MyObject with id #1 from the database if
- present, is now available. merge() still copies the state
- of the given object to the persistent one, so an example
- like the above would typically have copied "None" from all
- attributes of "x" onto the persistent copy. These can be
- reverted using session.expire(x).
-
- .. change::
- :tags: orm
- :tickets:
-
- Also fixed behavior in merge() whereby collection elements
- present on the destination but not the merged collection
- were not being removed from the destination.
-
- .. change::
- :tags: orm
- :tickets: 995
-
- Added a more aggressive check for "uncompiled mappers",
- helps particularly with declarative layer
-
- .. change::
- :tags: orm
- :tickets:
-
- The methodology behind "primaryjoin"/"secondaryjoin" has
- been refactored. Behavior should be slightly more
- intelligent, primarily in terms of error messages which
- have been pared down to be more readable. In a slight
- number of scenarios it can better resolve the correct
- foreign key than before.
-
- .. change::
- :tags: orm
- :tickets:
-
- Added comparable_property(), adds query Comparator
- behavior to regular, unmanaged Python properties
-
- .. change::
- :tags: orm, Company.employees.of_type(Engineer), 'machines'
- :tickets:
-
- the functionality of query.with_polymorphic() has
- been added to mapper() as a configuration option.
-
- It's set via several forms:
- with_polymorphic='*'
- with_polymorphic=[mappers]
- with_polymorphic=('*', selectable)
- with_polymorphic=([mappers], selectable)
-
- This controls the default polymorphic loading strategy
- for inherited mappers. When a selectable is not given,
- outer joins are created for all joined-table inheriting
- mappers requested. Note that the auto-create of joins
- is not compatible with concrete table inheritance.
-
- The existing select_table flag on mapper() is now
- deprecated and is synonymous with
- with_polymorphic('*', select_table). Note that the
- underlying "guts" of select_table have been
- completely removed and replaced with the newer,
- more flexible approach.
-
- The new approach also automatically allows eager loads
- to work for subclasses, if they are present, for
- example::
-
- sess.query(Company).options(eagerload_all())
-
- to load Company objects, their employees, and the
- 'machines' collection of employees who happen to be
- Engineers. A "with_polymorphic" Query option should be
- introduced soon as well which would allow per-Query
- control of with_polymorphic() on relations.
-
- .. change::
- :tags: orm
- :tickets:
-
- added two "experimental" features to Query,
- "experimental" in that their specific name/behavior
- is not carved in stone just yet: _values() and
- _from_self(). We'd like feedback on these.
-
- - _values(\*columns) is given a list of column
- expressions, and returns a new Query that only
- returns those columns. When evaluated, the return
- value is a list of tuples just like when using
- add_column() or add_entity(), the only difference is
- that "entity zero", i.e. the mapped class, is not
- included in the results. This means it finally makes
- sense to use group_by() and having() on Query, which
- have been sitting around uselessly until now.
-
- A future change to this method may include that its
- ability to join, filter and allow other options not
- related to a "resultset" are removed, so the feedback
- we're looking for is how people want to use
- _values()...i.e. at the very end, or do people prefer
- to continue generating after it's called.
-
- - _from_self() compiles the SELECT statement for the
- Query (minus any eager loaders), and returns a new
- Query that selects from that SELECT. So basically you
- can query from a Query without needing to extract the
- SELECT statement manually. This gives meaning to
- operations like query[3:5]._from_self().filter(some
- criterion). There's not much controversial here
- except that you can quickly create highly nested
- queries that are less efficient, and we want feedback
- on the naming choice.
-
- .. change::
- :tags: orm
- :tickets:
-
- query.order_by() and query.group_by() will accept
- multiple arguments using \*args (like select()
- already does).
-
- .. change::
- :tags: orm
- :tickets:
-
- Added some convenience descriptors to Query:
- query.statement returns the full SELECT construct,
- query.whereclause returns just the WHERE part of the
- SELECT construct.
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed/covered case when using a False/0 value as a
- polymorphic discriminator.
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed bug which was preventing synonym() attributes from
- being used with inheritance
-
- .. change::
- :tags: orm
- :tickets: 996
-
- Fixed SQL function truncation of trailing underscores
-
- .. change::
- :tags: orm
- :tickets:
-
- When attributes are expired on a pending instance, an
- error will not be raised when the "refresh" action is
- triggered and no result is found.
-
- .. change::
- :tags: orm
- :tickets:
-
- Session.execute can now find binds from metadata
-
- .. change::
- :tags: orm
- :tickets:
-
- Adjusted the definition of "self-referential" to be any
- two mappers with a common parent (this affects whether or
- not aliased=True is required when joining with Query).
-
- .. change::
- :tags: orm
- :tickets:
-
- Made some fixes to the "from_joinpoint" argument to
- query.join() so that if the previous join was aliased and
- this one isn't, the join still happens successfully.
-
- .. change::
- :tags: orm
- :tickets: 895
-
- Assorted "cascade deletes" fixes:
- - Fixed "cascade delete" operation of dynamic relations,
- which had only been implemented for foreign-key
- nulling behavior in 0.4.2 and not actual cascading
- deletes
-
- - Delete cascade without delete-orphan cascade on a
- many-to-one will not delete orphans which were
- disconnected from the parent before session.delete()
- is called on the parent (one-to-many already had
- this).
-
- - Delete cascade with delete-orphan will delete orphans
- whether or not it remains attached to its also-deleted
- parent.
-
- - delete-orphan cascade is properly detected on relations
- that are present on superclasses when using inheritance.
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed order_by calculation in Query to properly alias
- mapper-config'ed order_by when using select_from()
-
- .. change::
- :tags: orm
- :tickets:
-
- Refactored the diffing logic that kicks in when replacing
- one collection with another into collections.bulk_replace,
- useful to anyone building multi-level collections.
-
- .. change::
- :tags: orm
- :tickets:
-
- Cascade traversal algorithm converted from recursive to
- iterative to support deep object graphs.
-
- .. change::
- :tags: sql
- :tickets: 999
-
- schema-qualified tables now will place the schemaname
- ahead of the tablename in all column expressions as well
- as when generating column labels. This prevents cross-
- schema name collisions in all cases
-
- .. change::
- :tags: sql
- :tickets:
-
- can now allow selects which correlate all FROM clauses
- and have no FROM themselves. These are typically
- used in a scalar context, i.e. SELECT x, (SELECT x WHERE y)
- FROM table. Requires explicit correlate() call.
-
- .. change::
- :tags: sql
- :tickets:
-
- 'name' is no longer a required constructor argument for
- Column(). It (and .key) may now be deferred until the
- column is added to a Table.
-
- .. change::
- :tags: sql
- :tickets: 791, 993
-
- like(), ilike(), contains(), startswith(), endswith() take
- an optional keyword argument "escape=<somestring>", which
- is set as the escape character using the syntax "x LIKE y
- ESCAPE '<somestring>'".
-
- .. change::
- :tags: sql
- :tickets:
-
- random() is now a generic sql function and will compile to
- the database's random implementation, if any.
-
- .. change::
- :tags: sql
- :tickets:
-
- update().values() and insert().values() take keyword
- arguments.
-
- .. change::
- :tags: sql
- :tickets:
-
- Fixed an issue in select() regarding its generation of
- FROM clauses, in rare circumstances two clauses could be
- produced when one was intended to cancel out the other.
- Some ORM queries with lots of eager loads might have seen
- this symptom.
-
- .. change::
- :tags: sql
- :tickets:
-
- The case() function now also takes a dictionary as its
- whens parameter. It also interprets the "THEN"
- expressions as values by default, meaning case([(x==y,
- "foo")]) will interpret "foo" as a bound value, not a SQL
- expression. use text(expr) for literal SQL expressions in
- this case. For the criterion itself, these may be literal
- strings only if the "value" keyword is present, otherwise
- SA will force explicit usage of either text() or
- literal().
-
- .. change::
- :tags: oracle
- :tickets:
-
- The "owner" keyword on Table is now deprecated, and is
- exactly synonymous with the "schema" keyword. Tables can
- now be reflected with alternate "owner" attributes,
- explicitly stated on the Table object or not using
- "schema".
-
- .. change::
- :tags: oracle
- :tickets:
-
- All of the "magic" searching for synonyms, DBLINKs etc.
- during table reflection are disabled by default unless you
- specify "oracle_resolve_synonyms=True" on the Table
- object. Resolving synonyms necessarily leads to some
- messy guessing which we'd rather leave off by default.
- When the flag is set, tables and related tables will be
- resolved against synonyms in all cases, meaning if a
- synonym exists for a particular table, reflection will use
- it when reflecting related tables. This is stickier
- behavior than before which is why it's off by default.
-
- .. change::
- :tags: declarative, extension
- :tickets:
-
- The "synonym" function is now directly usable with
- "declarative". Pass in the decorated property using the
- "descriptor" keyword argument, e.g.: somekey =
- synonym('_somekey', descriptor=property(g, s))
-
- .. change::
- :tags: declarative, extension
- :tickets:
-
- The "deferred" function is usable with "declarative".
- Simplest usage is to declare deferred and Column together,
- e.g.: data = deferred(Column(Text))
-
- .. change::
- :tags: declarative, extension
- :tickets:
-
- Declarative also gained @synonym_for(...) and
- @comparable_using(...), front-ends for synonym and
- comparable_property.
-
- .. change::
- :tags: declarative, extension
- :tickets: 995
-
- Improvements to mapper compilation when using declarative;
- already-compiled mappers will still trigger compiles of
- other uncompiled mappers when used
-
- .. change::
- :tags: declarative, extension
- :tickets:
-
- Declarative will complete setup for Columns lacking names,
- allows a more DRY syntax.
-
- class Foo(Base):
- __tablename__ = 'foos'
- id = Column(Integer, primary_key=True)
-
- .. change::
- :tags: declarative, extension
- :tickets:
-
- inheritance in declarative can be disabled when sending
- "inherits=None" to __mapper_args__.
-
- .. change::
- :tags: declarative, extension
- :tickets:
-
- declarative_base() takes optional kwarg "mapper", which
- is any callable/class/method that produces a mapper,
- such as declarative_base(mapper=scopedsession.mapper).
- This property can also be set on individual declarative
- classes using the "__mapper_cls__" property.
-
- .. change::
- :tags: postgres
- :tickets: 1001
-
- Got PG server side cursors back into shape, added fixed
- unit tests as part of the default test suite. Added
- better uniqueness to the cursor ID
-
- .. change::
- :tags: oracle
- :tickets:
-
- The "owner" keyword on Table is now deprecated, and is
- exactly synonymous with the "schema" keyword. Tables can
- now be reflected with alternate "owner" attributes,
- explicitly stated on the Table object or not using
- "schema".
-
- .. change::
- :tags: oracle
- :tickets:
-
- All of the "magic" searching for synonyms, DBLINKs etc.
- during table reflection are disabled by default unless you
- specify "oracle_resolve_synonyms=True" on the Table
- object. Resolving synonyms necessarily leads to some
- messy guessing which we'd rather leave off by default.
- When the flag is set, tables and related tables will be
- resolved against synonyms in all cases, meaning if a
- synonym exists for a particular table, reflection will use
- it when reflecting related tables. This is stickier
- behavior than before which is why it's off by default.
-
- .. change::
- :tags: mssql
- :tickets: 979
-
- Reflected tables will now automatically load other tables
- which are referenced by Foreign keys in the auto-loaded
- table,.
-
- .. change::
- :tags: mssql
- :tickets: 916
-
- Added executemany check to skip identity fetch,.
-
- .. change::
- :tags: mssql
- :tickets: 884
-
- Added stubs for small date type.
-
- .. change::
- :tags: mssql
- :tickets:
-
- Added a new 'driver' keyword parameter for the pyodbc dialect.
- Will substitute into the ODBC connection string if given,
- defaults to 'SQL Server'.
-
- .. change::
- :tags: mssql
- :tickets:
-
- Added a new 'max_identifier_length' keyword parameter for
- the pyodbc dialect.
-
- .. change::
- :tags: mssql
- :tickets:
-
- Improvements to pyodbc + Unix. If you couldn't get that
- combination to work before, please try again.
-
- .. change::
- :tags: mysql
- :tickets:
-
- The connection.info keys the dialect uses to cache server
- settings have changed and are now namespaced.
-
-.. changelog::
- :version: 0.4.4
- :released: Wed Mar 12 2008
-
- .. change::
- :tags: sql
- :tickets: 975
-
- Can again create aliases of selects against textual FROM
- clauses.
-
- .. change::
- :tags: sql
- :tickets:
-
- The value of a bindparam() can be a callable, in which
- case it's evaluated at statement execution time to get the
- value.
-
- .. change::
- :tags: sql
- :tickets: 978
-
- Added exception wrapping/reconnect support to result set
- fetching. Reconnect works for those databases that raise
- a catchable data error during results (i.e. doesn't work
- on MySQL)
-
- .. change::
- :tags: sql
- :tickets: 936
-
- Implemented two-phase API for "threadlocal" engine, via
- engine.begin_twophase(), engine.prepare()
-
- .. change::
- :tags: sql
- :tickets: 986
-
- Fixed bug which was preventing UNIONS from being
- cloneable.
-
- .. change::
- :tags: sql
- :tickets:
-
- Added "bind" keyword argument to insert(), update(),
- delete() and DDL(). The .bind property is now assignable
- on those statements as well as on select().
-
- .. change::
- :tags: sql
- :tickets:
-
- Insert statements can now be compiled with extra "prefix"
- words between INSERT and INTO, for vendor extensions like
- MySQL's INSERT IGNORE INTO table.
-
- .. change::
- :tags: orm
- :tickets:
-
- any(), has(), contains(), ~contains(), attribute level ==
- and != now work properly with self-referential relations -
- the clause inside the EXISTS is aliased on the "remote"
- side to distinguish it from the parent table. This
- applies to single table self-referential as well as
- inheritance-based self-referential.
-
- .. change::
- :tags: orm
- :tickets: 985
-
- Repaired behavior of == and != operators at the relation()
- level when compared against NULL for one-to-one relations
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed bug whereby session.expire() attributes were not
- loading on an polymorphically-mapped instance mapped by a
- select_table mapper.
-
- .. change::
- :tags: orm
- :tickets:
-
- Added query.with_polymorphic() - specifies a list of
- classes which descend from the base class, which will be
- added to the FROM clause of the query. Allows subclasses
- to be used within filter() criterion as well as eagerly
- loads the attributes of those subclasses.
-
- .. change::
- :tags: orm
- :tickets:
-
- Your cries have been heard: removing a pending item from
- an attribute or collection with delete-orphan expunges the
- item from the session; no FlushError is raised. Note that
- if you session.save()'ed the pending item explicitly, the
- attribute/collection removal still knocks it out.
-
- .. change::
- :tags: orm
- :tickets:
-
- session.refresh() and session.expire() raise an error when
- called on instances which are not persistent within the
- session
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed potential generative bug when the same Query was
- used to generate multiple Query objects using join().
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed bug which was introduced in 0.4.3, whereby loading
- an already-persistent instance mapped with joined table
- inheritance would trigger a useless "secondary" load from
- its joined table, when using the default "select"
- polymorphic_fetch. This was due to attributes being
- marked as expired during its first load and not getting
- unmarked from the previous "secondary" load. Attributes
- are now unexpired based on presence in __dict__ after any
- load or commit operation succeeds.
-
- .. change::
- :tags: orm
- :tickets:
-
- Deprecated Query methods apply_sum(), apply_max(),
- apply_min(), apply_avg(). Better methodologies are
- coming....
-
- .. change::
- :tags: orm
- :tickets:
-
- relation() can accept a callable for its first argument,
- which returns the class to be related. This is in place
- to assist declarative packages to define relations without
- classes yet being in place.
-
- .. change::
- :tags: orm
- :tickets:
-
- Added a new "higher level" operator called "of_type()":
- used in join() as well as with any() and has(), qualifies
- the subclass which will be used in filter criterion, e.g.:
-
- query.filter(Company.employees.of_type(Engineer).
- any(Engineer.name=='foo'))
-
- or
-
- query.join(Company.employees.of_type(Engineer)).
- filter(Engineer.name=='foo')
-
- .. change::
- :tags: orm
- :tickets:
-
- Preventive code against a potential lost-reference bug in
- flush().
-
- .. change::
- :tags: orm
- :tickets:
-
- Expressions used in filter(), filter_by() and others, when
- they make usage of a clause generated from a relation
- using the identity of a child object (e.g.,
- filter(Parent.child==<somechild>)), evaluate the actual
- primary key value of <somechild> at execution time so that
- the autoflush step of the Query can complete, thereby
- populating the PK value of <somechild> in the case that
- <somechild> was pending.
-
- .. change::
- :tags: orm
- :tickets:
-
- setting the relation()-level order by to a column in the
- many-to-many "secondary" table will now work with eager
- loading, previously the "order by" wasn't aliased against
- the secondary table's alias.
-
- .. change::
- :tags: orm
- :tickets:
-
- Synonyms riding on top of existing descriptors are now
- full proxies to those descriptors.
-
- .. change::
- :tags: dialects
- :tickets:
-
- Invalid SQLite connection URLs now raise an error.
-
- .. change::
- :tags: dialects
- :tickets: 981
-
- postgres TIMESTAMP renders correctly
-
- .. change::
- :tags: dialects
- :tickets:
-
- postgres PGArray is a "mutable" type by default; when used
- with the ORM, mutable-style equality/ copy-on-write
- techniques are used to test for changes.
-
- .. change::
- :tags: extensions
- :tickets:
-
- a new super-small "declarative" extension has been added,
- which allows Table and mapper() configuration to take
- place inline underneath a class declaration. This
- extension differs from ActiveMapper and Elixir in that it
- does not redefine any SQLAlchemy semantics at all; literal
- Column, Table and relation() constructs are used to define
- the class behavior and table definition.
-
-.. changelog::
- :version: 0.4.3
- :released: Thu Feb 14 2008
-
- .. change::
- :tags: sql
- :tickets:
-
- Added "schema.DDL", an executable free-form DDL statement.
- DDLs can be executed in isolation or attached to Table or
- MetaData instances and executed automatically when those
- objects are created and/or dropped.
-
- .. change::
- :tags: sql
- :tickets:
-
- Table columns and constraints can be overridden on a an
- existing table (such as a table that was already reflected)
- using the 'useexisting=True' flag, which now takes into
- account the arguments passed along with it.
-
- .. change::
- :tags: sql
- :tickets:
-
- Added a callable-based DDL events interface, adds hooks
- before and after Tables and MetaData create and drop.
-
- .. change::
- :tags: sql
- :tickets:
-
- Added generative where(<criterion>) method to delete() and
- update() constructs which return a new object with criterion
- joined to existing criterion via AND, just like
- select().where().
-
- .. change::
- :tags: sql
- :tickets: 727
-
- Added "ilike()" operator to column operations. Compiles to
- ILIKE on postgres, lower(x) LIKE lower(y) on all
- others.
-
- .. change::
- :tags: sql
- :tickets: 943
-
- Added "now()" as a generic function; on SQLite, Oracle
- and MSSQL compiles as "CURRENT_TIMESTAMP"; "now()" on
- all others.
-
- .. change::
- :tags: sql
- :tickets: 962
-
- The startswith(), endswith(), and contains() operators now
- concatenate the wildcard operator with the given operand in
- SQL, i.e. "'%' || <bindparam>" in all cases, accept
- text('something') operands properly
-
- .. change::
- :tags: sql
- :tickets: 962
-
- cast() accepts text('something') and other non-literal
- operands properly
-
- .. change::
- :tags: sql
- :tickets:
-
- fixed bug in result proxy where anonymously generated
- column labels would not be accessible using their straight
- string name
-
- .. change::
- :tags: sql
- :tickets:
-
- Deferrable constraints can now be defined.
-
- .. change::
- :tags: sql
- :tickets: 915
-
- Added "autocommit=True" keyword argument to select() and
- text(), as well as generative autocommit() method on
- select(); for statements which modify the database through
- some user-defined means other than the usual INSERT/UPDATE/
- DELETE etc. This flag will enable "autocommit" behavior
- during execution if no transaction is in progress.
-
- .. change::
- :tags: sql
- :tickets:
-
- The '.c.' attribute on a selectable now gets an entry for
- every column expression in its columns clause. Previously,
- "unnamed" columns like functions and CASE statements weren't
- getting put there. Now they will, using their full string
- representation if no 'name' is available.
-
- .. change::
- :tags: sql
- :tickets:
-
- a CompositeSelect, i.e. any union(), union_all(),
- intersect(), etc. now asserts that each selectable contains
- the same number of columns. This conforms to the
- corresponding SQL requirement.
-
- .. change::
- :tags: sql
- :tickets:
-
- The anonymous 'label' generated for otherwise unlabeled
- functions and expressions now propagates outwards at compile
- time for expressions like select([select([func.foo()])]).
-
- .. change::
- :tags: sql
- :tickets:
-
- Building on the above ideas, CompositeSelects now build up
- their ".c." collection based on the names present in the
- first selectable only; corresponding_column() now works
- fully for all embedded selectables.
-
- .. change::
- :tags: sql
- :tickets:
-
- Oracle and others properly encode SQL used for defaults like
- sequences, etc., even if no unicode idents are used since
- identifier preparer may return a cached unicode identifier.
-
- .. change::
- :tags: sql
- :tickets:
-
- Column and clause comparisons to datetime objects on the
- left hand side of the expression now work (d < table.c.col).
- (datetimes on the RHS have always worked, the LHS exception
- is a quirk of the datetime implementation.)
-
- .. change::
- :tags: orm
- :tickets:
-
- Every Session.begin() must now be accompanied by a
- corresponding commit() or rollback() unless the session is
- closed with Session.close(). This also includes the begin()
- which is implicit to a session created with
- transactional=True. The biggest change introduced here is
- that when a Session created with transactional=True raises
- an exception during flush(), you must call
- Session.rollback() or Session.close() in order for that
- Session to continue after an exception.
-
- .. change::
- :tags: orm
- :tickets: 961
-
- Fixed merge() collection-doubling bug when merging transient
- entities with backref'ed collections.
-
- .. change::
- :tags: orm
- :tickets:
-
- merge(dont_load=True) does not accept transient entities,
- this is in continuation with the fact that
- merge(dont_load=True) does not accept any "dirty" objects
- either.
-
- .. change::
- :tags: orm
- :tickets:
-
- Added standalone "query" class attribute generated by a
- scoped_session. This provides MyClass.query without using
- Session.mapper. Use via:
-
- MyClass.query = Session.query_property()
-
- .. change::
- :tags: orm
- :tickets:
-
- The proper error message is raised when trying to access
- expired instance attributes with no session present
-
- .. change::
- :tags: orm
- :tickets:
-
- dynamic_loader() / lazy="dynamic" now accepts and uses
- the order_by parameter in the same way in which it works
- with relation().
-
- .. change::
- :tags: orm
- :tickets:
-
- Added expire_all() method to Session. Calls expire() for
- all persistent instances. This is handy in conjunction
- with...
-
- .. change::
- :tags: orm
- :tickets:
-
- Instances which have been partially or fully expired will
- have their expired attributes populated during a regular
- Query operation which affects those objects, preventing a
- needless second SQL statement for each instance.
-
- .. change::
- :tags: orm
- :tickets: 938
-
- Dynamic relations, when referenced, create a strong
- reference to the parent object so that the query still has a
- parent to call against even if the parent is only created
- (and otherwise dereferenced) within the scope of a single
- expression.
-
- .. change::
- :tags: orm
- :tickets:
-
- Added a mapper() flag "eager_defaults". When set to True,
- defaults that are generated during an INSERT or UPDATE
- operation are post-fetched immediately, instead of being
- deferred until later. This mimics the old 0.3 behavior.
-
- .. change::
- :tags: orm
- :tickets:
-
- query.join() can now accept class-mapped attributes as
- arguments. These can be used in place or in any combination
- with strings. In particular this allows construction of
- joins to subclasses on a polymorphic relation, i.e.:
-
- query(Company).join(['employees', Engineer.name])
-
- .. change::
- :tags: orm, ('employees', people.join(engineer)), Engineer.name
- :tickets:
-
- query.join() can also accept tuples of attribute name/some
- selectable as arguments. This allows construction of joins
- *from* subclasses of a polymorphic relation, i.e.:
-
- query(Company).\
- join(
-
- )
-
- .. change::
- :tags: orm
- :tickets:
-
- General improvements to the behavior of join() in
- conjunction with polymorphic mappers, i.e. joining from/to
- polymorphic mappers and properly applying aliases.
-
- .. change::
- :tags: orm
- :tickets: 933
-
- Fixed/improved behavior when a mapper determines the natural
- "primary key" of a mapped join, it will more effectively
- reduce columns which are equivalent via foreign key
- relation. This affects how many arguments need to be sent
- to query.get(), among other things.
-
- .. change::
- :tags: orm
- :tickets: 946
-
- The lazy loader can now handle a join condition where the
- "bound" column (i.e. the one that gets the parent id sent as
- a bind parameter) appears more than once in the join
- condition. Specifically this allows the common task of a
- relation() which contains a parent-correlated subquery, such
- as "select only the most recent child item".
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed bug in polymorphic inheritance where an incorrect
- exception is raised when base polymorphic_on column does not
- correspond to any columns within the local selectable of an
- inheriting mapper more than one level deep
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed bug in polymorphic inheritance which made it difficult
- to set a working "order_by" on a polymorphic mapper.
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed a rather expensive call in Query that was slowing down
- polymorphic queries.
-
- .. change::
- :tags: orm
- :tickets: 954
-
- "Passive defaults" and other "inline" defaults can now be
- loaded during a flush() call if needed; in particular, this
- allows constructing relations() where a foreign key column
- references a server-side-generated, non-primary-key
- column.
-
- .. change::
- :tags: orm
- :tickets:
-
- Additional Session transaction fixes/changes:
- - Fixed bug with session transaction management: parent
- transactions weren't started on the connection when
- adding a connection to a nested transaction.
-
- - session.transaction now always refers to the innermost
- active transaction, even when commit/rollback are called
- directly on the session transaction object.
-
- - Two-phase transactions can now be prepared.
-
- - When preparing a two-phase transaction fails on one
- connection, all the connections are rolled back.
-
- - session.close() didn't close all transactions when
- nested transactions were used.
-
- - rollback() previously erroneously set the current
- transaction directly to the parent of the transaction
- that could be rolled back to. Now it rolls back the next
- transaction up that can handle it, but sets the current
- transaction to its parent and inactivates the
- transactions in between. Inactive transactions can only
- be rolled back or closed, any other call results in an
- error.
-
- - autoflush for commit() wasn't flushing for simple
- subtransactions.
-
- - unitofwork flush didn't close the failed transaction
- when the session was not in a transaction and committing
- the transaction failed.
-
- .. change::
- :tags: orm
- :tickets: 964, 940
-
- Miscellaneous tickets:
-
- .. change::
- :tags: general
- :tickets:
-
- Fixed a variety of hidden and some not-so-hidden
- compatibility issues for Python 2.3, thanks to new support
- for running the full test suite on 2.3.
-
- .. change::
- :tags: general
- :tickets:
-
- Warnings are now issued as type exceptions.SAWarning.
-
- .. change::
- :tags: dialects
- :tickets:
-
- Better support for schemas in SQLite (linked in by ATTACH
- DATABASE ... AS name). In some cases in the past, schema
- names were omitted from generated SQL for SQLite. This is
- no longer the case.
-
- .. change::
- :tags: dialects
- :tickets:
-
- table_names on SQLite now picks up temporary tables as well.
-
- .. change::
- :tags: dialects
- :tickets:
-
- Auto-detect an unspecified MySQL ANSI_QUOTES mode during
- reflection operations, support for changing the mode
- midstream. Manual mode setting is still required if no
- reflection is used.
-
- .. change::
- :tags: dialects
- :tickets:
-
- Fixed reflection of TIME columns on SQLite.
-
- .. change::
- :tags: dialects
- :tickets: 580
-
- Finally added PGMacAddr type to postgres
-
- .. change::
- :tags: dialects
- :tickets:
-
- Reflect the sequence associated to a PK field (typically
- with a BEFORE INSERT trigger) under Firebird
-
- .. change::
- :tags: dialects
- :tickets: 941
-
- Oracle assembles the correct columns in the result set
- column mapping when generating a LIMIT/OFFSET subquery,
- allows columns to map properly to result sets even if
- long-name truncation kicks in
-
- .. change::
- :tags: dialects
- :tickets:
-
- MSSQL now includes EXEC in the _is_select regexp, which
- should allow row-returning stored procedures to be used.
-
- .. change::
- :tags: dialects
- :tickets:
-
- MSSQL now includes an experimental implementation of
- LIMIT/OFFSET using the ANSI SQL row_number() function, so it
- requires MSSQL-2005 or higher. To enable the feature, add
- "has_window_funcs" to the keyword arguments for connect, or
- add "?has_window_funcs=1" to your dburi query arguments.
-
- .. change::
- :tags: ext
- :tickets:
-
- Changed ext.activemapper to use a non-transactional session
- for the objectstore.
-
- .. change::
- :tags: ext
- :tickets:
-
- Fixed output order of "['a'] + obj.proxied" binary operation
- on association-proxied lists.
-
-.. changelog::
- :version: 0.4.2p3
- :released: Wed Jan 09 2008
-
- .. change::
- :tags: general
- :tickets:
-
- sub version numbering scheme changed to suite
- setuptools version number rules; easy_install -u
- should now get this version over 0.4.2.
-
- .. change::
- :tags: sql
- :tickets: 912
-
- Text type is properly exported now and does not
- raise a warning on DDL create; String types with no
- length only raise warnings during CREATE TABLE
-
- .. change::
- :tags: sql
- :tickets:
-
- new UnicodeText type is added, to specify an
- encoded, unlengthed Text type
-
- .. change::
- :tags: sql
- :tickets:
-
- fixed bug in union() so that select() statements
- which don't derive from FromClause objects can be
- unioned
-
- .. change::
- :tags: orm
- :tickets:
-
- fixed bug with session.dirty when using "mutable
- scalars" (such as PickleTypes)
-
- .. change::
- :tags: orm
- :tickets:
-
- added a more descriptive error message when flushing
- on a relation() that has non-locally-mapped columns
- in its primary or secondary join condition
-
- .. change::
- :tags: dialects
- :tickets:
-
- Fixed reflection of mysql empty string column
- defaults.
-
- .. change::
- :tags: sql
- :tickets: 912
-
- changed name of TEXT to Text since its a "generic"
- type; TEXT name is deprecated until 0.5. The
- "upgrading" behavior of String to Text when no
- length is present is also deprecated until 0.5; will
- issue a warning when used for CREATE TABLE
- statements (String with no length for SQL expression
- purposes is still fine)
-
- .. change::
- :tags: sql
- :tickets: 924
-
- generative select.order_by(None) / group_by(None)
- was not managing to reset order by/group by
- criterion, fixed
-
- .. change::
- :tags: orm
- :tickets:
-
- suppressing *all* errors in
- InstanceState.__cleanup() now.
-
- .. change::
- :tags: orm
- :tickets: 922
-
- fixed an attribute history bug whereby assigning a
- new collection to a collection-based attribute which
- already had pending changes would generate incorrect
- history
-
- .. change::
- :tags: orm
- :tickets: 925
-
- fixed delete-orphan cascade bug whereby setting the
- same object twice to a scalar attribute could log it
- as an orphan
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed cascades on a += assignment to a list-based
- relation.
-
- .. change::
- :tags: orm
- :tickets: 919
-
- synonyms can now be created against props that don't
- exist yet, which are later added via add_property().
- This commonly includes backrefs. (i.e. you can make
- synonyms for backrefs without worrying about the
- order of operations)
-
- .. change::
- :tags: orm
- :tickets:
-
- fixed bug which could occur with polymorphic "union"
- mapper which falls back to "deferred" loading of
- inheriting tables
-
- .. change::
- :tags: orm
- :tickets:
-
- the "columns" collection on a mapper/mapped class
- (i.e. 'c') is against the mapped table, not the
- select_table in the case of polymorphic "union"
- loading (this shouldn't be noticeable).
-
- .. change::
- :tags: ext
- :tickets:
-
- '+', '*', '+=' and '\*=' support for association
- proxied lists.
-
- .. change::
- :tags: dialects
- :tickets: 923
-
- mssql - narrowed down the test for "date"/"datetime"
- in MSDate/ MSDateTime subclasses so that incoming
- "datetime" objects don't get mis-interpreted as
- "date" objects and vice versa.
-
- .. change::
- :tags: orm
- :tickets:
-
- fixed fairly critical bug whereby the same instance could be listed
- more than once in the unitofwork.new collection; most typically
- reproduced when using a combination of inheriting mappers and
- ScopedSession.mapper, as the multiple __init__ calls per instance
- could save() the object with distinct _state objects
-
- .. change::
- :tags: orm
- :tickets:
-
- added very rudimentary yielding iterator behavior to Query. Call
- query.yield_per(<number of rows>) and evaluate the Query in an
- iterative context; every collection of N rows will be packaged up
- and yielded. Use this method with extreme caution since it does
- not attempt to reconcile eagerly loaded collections across
- result batch boundaries, nor will it behave nicely if the same
- instance occurs in more than one batch. This means that an eagerly
- loaded collection will get cleared out if it's referenced in more than
- one batch, and in all cases attributes will be overwritten on instances
- that occur in more than one batch.
-
- .. change::
- :tags: orm
- :tickets: 920
-
- Fixed in-place set mutation operators for set collections and association
- proxied sets.
-
- .. change::
- :tags: dialects
- :tickets: 913
-
- Fixed the missing call to subtype result processor for the PGArray
- type.
-
-.. changelog::
- :version: 0.4.2
- :released: Wed Jan 02 2008
-
- .. change::
- :tags: sql
- :tickets: 615
-
- generic functions ! we introduce a database of known SQL functions, such
- as current_timestamp, coalesce, and create explicit function objects
- representing them. These objects have constrained argument lists, are
- type aware, and can compile in a dialect-specific fashion. So saying
- func.char_length("foo", "bar") raises an error (too many args),
- func.coalesce(datetime.date(2007, 10, 5), datetime.date(2005, 10, 15))
- knows that its return type is a Date. We only have a few functions
- represented so far but will continue to add to the system
-
- .. change::
- :tags: sql
- :tickets:
-
- auto-reconnect support improved; a Connection can now automatically
- reconnect after its underlying connection is invalidated, without
- needing to connect() again from the engine. This allows an ORM session
- bound to a single Connection to not need a reconnect.
- Open transactions on the Connection must be rolled back after an invalidation
- of the underlying connection else an error is raised. Also fixed
- bug where disconnect detect was not being called for cursor(), rollback(),
- or commit().
-
- .. change::
- :tags: sql
- :tickets:
-
- added new flag to String and create_engine(),
- assert_unicode=(True|False|'warn'\|None). Defaults to `False` or `None` on
- create_engine() and String, `'warn'` on the Unicode type. When `True`,
- results in all unicode conversion operations raising an exception when a
- non-unicode bytestring is passed as a bind parameter. 'warn' results
- in a warning. It is strongly advised that all unicode-aware applications
- make proper use of Python unicode objects (i.e. u'hello' and not 'hello')
- so that data round trips accurately.
-
- .. change::
- :tags: sql
- :tickets:
-
- generation of "unique" bind parameters has been simplified to use the same
- "unique identifier" mechanisms as everything else. This doesn't affect
- user code, except any code that might have been hardcoded against the generated
- names. Generated bind params now have the form "<paramname>_<num>",
- whereas before only the second bind of the same name would have this form.
-
- .. change::
- :tags: sql
- :tickets:
-
- select().as_scalar() will raise an exception if the select does not have
- exactly one expression in its columns clause.
-
- .. change::
- :tags: sql
- :tickets:
-
- bindparam() objects themselves can be used as keys for execute(), i.e.
- statement.execute({bind1:'foo', bind2:'bar'})
-
- .. change::
- :tags: sql
- :tickets:
-
- added new methods to TypeDecorator, process_bind_param() and
- process_result_value(), which automatically take advantage of the processing
- of the underlying type. Ideal for using with Unicode or Pickletype.
- TypeDecorator should now be the primary way to augment the behavior of any
- existing type including other TypeDecorator subclasses such as PickleType.
-
- .. change::
- :tags: sql
- :tickets:
-
- selectables (and others) will issue a warning when two columns in
- their exported columns collection conflict based on name.
-
- .. change::
- :tags: sql
- :tickets: 890
-
- tables with schemas can still be used in sqlite, firebird,
- schema name just gets dropped
-
- .. change::
- :tags: sql
- :tickets:
-
- changed the various "literal" generation functions to use an anonymous
- bind parameter. not much changes here except their labels now look
- like ":param_1", ":param_2" instead of ":literal"
-
- .. change::
- :tags: sql
- :tickets:
-
- column labels in the form "tablename.columname", i.e. with a dot, are now
- supported.
-
- .. change::
- :tags: sql
- :tickets:
-
- from_obj keyword argument to select() can be a scalar or a list.
-
- .. change::
- :tags: orm
- :tickets: 871
-
- a major behavioral change to collection-based backrefs: they no
- longer trigger lazy loads ! "reverse" adds and removes
- are queued up and are merged with the collection when it is
- actually read from and loaded; but do not trigger a load beforehand.
- For users who have noticed this behavior, this should be much more
- convenient than using dynamic relations in some cases; for those who
- have not, you might notice your apps using a lot fewer queries than
- before in some situations.
-
- .. change::
- :tags: orm
- :tickets:
-
- mutable primary key support is added. primary key columns can be
- changed freely, and the identity of the instance will change upon
- flush. In addition, update cascades of foreign key referents (primary
- key or not) along relations are supported, either in tandem with the
- database's ON UPDATE CASCADE (required for DB's like Postgres) or
- issued directly by the ORM in the form of UPDATE statements, by setting
- the flag "passive_cascades=False".
-
- .. change::
- :tags: orm
- :tickets: 490
-
- inheriting mappers now inherit the MapperExtensions of their parent
- mapper directly, so that all methods for a particular MapperExtension
- are called for subclasses as well. As always, any MapperExtension
- can return either EXT_CONTINUE to continue extension processing
- or EXT_STOP to stop processing. The order of mapper resolution is:
- <extensions declared on the classes mapper> <extensions declared on the
- classes' parent mapper> <globally declared extensions>.
-
- Note that if you instantiate the same extension class separately
- and then apply it individually for two mappers in the same inheritance
- chain, the extension will be applied twice to the inheriting class,
- and each method will be called twice.
-
- To apply a mapper extension explicitly to each inheriting class but
- have each method called only once per operation, use the same
- instance of the extension for both mappers.
-
- .. change::
- :tags: orm
- :tickets: 907
-
- MapperExtension.before_update() and after_update() are now called
- symmetrically; previously, an instance that had no modified column
- attributes (but had a relation() modification) could be called with
- before_update() but not after_update()
-
- .. change::
- :tags: orm
- :tickets:
-
- columns which are missing from a Query's select statement
- now get automatically deferred during load.
-
- .. change::
- :tags: orm
- :tickets: 908
-
- mapped classes which extend "object" and do not provide an
- __init__() method will now raise TypeError if non-empty \*args
- or \**kwargs are present at instance construction time (and are
- not consumed by any extensions such as the scoped_session mapper),
- consistent with the behavior of normal Python classes
-
- .. change::
- :tags: orm
- :tickets: 899
-
- fixed Query bug when filter_by() compares a relation against None
-
- .. change::
- :tags: orm
- :tickets:
-
- improved support for pickling of mapped entities. Per-instance
- lazy/deferred/expired callables are now serializable so that
- they serialize and deserialize with _state.
-
- .. change::
- :tags: orm
- :tickets: 801
-
- new synonym() behavior: an attribute will be placed on the mapped
- class, if one does not exist already, in all cases. if a property
- already exists on the class, the synonym will decorate the property
- with the appropriate comparison operators so that it can be used in
- column expressions just like any other mapped attribute (i.e. usable in
- filter(), etc.) the "proxy=True" flag is deprecated and no longer means
- anything. Additionally, the flag "map_column=True" will automatically
- generate a ColumnProperty corresponding to the name of the synonym,
- i.e.: 'somename':synonym('_somename', map_column=True) will map the
- column named 'somename' to the attribute '_somename'. See the example
- in the mapper docs.
-
- .. change::
- :tags: orm
- :tickets:
-
- Query.select_from() now replaces all existing FROM criterion with
- the given argument; the previous behavior of constructing a list
- of FROM clauses was generally not useful as is required
- filter() calls to create join criterion, and new tables introduced
- within filter() already add themselves to the FROM clause. The
- new behavior allows not just joins from the main table, but select
- statements as well. Filter criterion, order bys, eager load
- clauses will be "aliased" against the given statement.
-
- .. change::
- :tags: orm
- :tickets:
-
- this month's refactoring of attribute instrumentation changes
- the "copy-on-load" behavior we've had since midway through 0.3
- with "copy-on-modify" in most cases. This takes a sizable chunk
- of latency out of load operations and overall does less work
- as only attributes which are actually modified get their
- "committed state" copied. Only "mutable scalar" attributes
- (i.e. a pickled object or other mutable item), the reason for
- the copy-on-load change in the first place, retain the old
- behavior.
-
- .. change::
- :tags: attrname, orm
- :tickets:
-
- a slight behavioral change to attributes is, del'ing an attribute
- does *not* cause the lazyloader of that attribute to fire off again;
- the "del" makes the effective value of the attribute "None". To
- re-trigger the "loader" for an attribute, use
- session.expire(instance,).
-
- .. change::
- :tags: orm
- :tickets:
-
- query.filter(SomeClass.somechild == None), when comparing
- a many-to-one property to None, properly generates "id IS NULL"
- including that the NULL is on the right side.
-
- .. change::
- :tags: orm
- :tickets:
-
- query.order_by() takes into account aliased joins, i.e.
- query.join('orders', aliased=True).order_by(Order.id)
-
- .. change::
- :tags: orm
- :tickets:
-
- eagerload(), lazyload(), eagerload_all() take an optional
- second class-or-mapper argument, which will select the mapper
- to apply the option towards. This can select among other
- mappers which were added using add_entity().
-
- .. change::
- :tags: orm
- :tickets:
-
- eagerloading will work with mappers added via add_entity().
-
- .. change::
- :tags: orm
- :tickets:
-
- added "cascade delete" behavior to "dynamic" relations just like
- that of regular relations. if passive_deletes flag (also just added)
- is not set, a delete of the parent item will trigger a full load of
- the child items so that they can be deleted or updated accordingly.
-
- .. change::
- :tags: orm
- :tickets:
-
- also with dynamic, implemented correct count() behavior as well
- as other helper methods.
-
- .. change::
- :tags: orm
- :tickets:
-
- fix to cascades on polymorphic relations, such that cascades
- from an object to a polymorphic collection continue cascading
- along the set of attributes specific to each element in the collection.
-
- .. change::
- :tags: orm
- :tickets: 893
-
- query.get() and query.load() do not take existing filter or other
- criterion into account; these methods *always* look up the given id
- in the database or return the current instance from the identity map,
- disregarding any existing filter, join, group_by or other criterion
- which has been configured.
-
- .. change::
- :tags: orm
- :tickets: 883
-
- added support for version_id_col in conjunction with inheriting mappers.
- version_id_col is typically set on the base mapper in an inheritance
- relationship where it takes effect for all inheriting mappers.
-
- .. change::
- :tags: orm
- :tickets:
-
- relaxed rules on column_property() expressions having labels; any
- ColumnElement is accepted now, as the compiler auto-labels non-labeled
- ColumnElements now. a selectable, like a select() statement, still
- requires conversion to ColumnElement via as_scalar() or label().
-
- .. change::
- :tags: orm
- :tickets:
-
- fixed backref bug where you could not del instance.attr if attr
- was None
-
- .. change::
- :tags: orm
- :tickets:
-
- several ORM attributes have been removed or made private:
- mapper.get_attr_by_column(), mapper.set_attr_by_column(),
- mapper.pks_by_table, mapper.cascade_callable(),
- MapperProperty.cascade_callable(), mapper.canload(),
- mapper.save_obj(), mapper.delete_obj(), mapper._mapper_registry,
- attributes.AttributeManager
-
- .. change::
- :tags: orm
- :tickets:
-
- Assigning an incompatible collection type to a relation attribute now
- raises TypeError instead of sqlalchemy's ArgumentError.
-
- .. change::
- :tags: orm
- :tickets: 886
-
- Bulk assignment of a MappedCollection now raises an error if a key in the
- incoming dictionary does not match the key that the collection's keyfunc
- would use for that value.
-
- .. change::
- :tags: orm, newval1, newval2
- :tickets:
-
- Custom collections can now specify a @converter method to translate
- objects used in "bulk" assignment into a stream of values, as in:
-
- .. sourcecode:: text
-
- obj.col =
- # or
- obj.dictcol = {'foo': newval1, 'bar': newval2}
-
- The MappedCollection uses this hook to ensure that incoming key/value
- pairs are sane from the collection's perspective.
-
- .. change::
- :tags: orm
- :tickets: 872
-
- fixed endless loop issue when using lazy="dynamic" on both
- sides of a bi-directional relationship
-
- .. change::
- :tags: orm
- :tickets: 904
-
- more fixes to the LIMIT/OFFSET aliasing applied with Query + eagerloads,
- in this case when mapped against a select statement
-
- .. change::
- :tags: orm
- :tickets:
-
- fix to self-referential eager loading such that if the same mapped
- instance appears in two or more distinct sets of columns in the same
- result set, its eagerly loaded collection will be populated regardless
- of whether or not all of the rows contain a set of "eager" columns for
- that collection. this would also show up as a KeyError when fetching
- results with join_depth turned on.
-
- .. change::
- :tags: orm
- :tickets:
-
- fixed bug where Query would not apply a subquery to the SQL when LIMIT
- was used in conjunction with an inheriting mapper where the eager
- loader was only in the parent mapper.
-
- .. change::
- :tags: orm
- :tickets:
-
- clarified the error message which occurs when you try to update()
- an instance with the same identity key as an instance already present
- in the session.
-
- .. change::
- :tags: orm
- :tickets:
-
- some clarifications and fixes to merge(instance, dont_load=True).
- fixed bug where lazy loaders were getting disabled on returned instances.
- Also, we currently do not support merging an instance which has uncommitted
- changes on it, in the case that dont_load=True is used....this will
- now raise an error. This is due to complexities in merging the
- "committed state" of the given instance to correctly correspond to the
- newly copied instance, as well as other modified state.
- Since the use case for dont_load=True is caching, the given instances
- shouldn't have any uncommitted changes on them anyway.
- We also copy the instances over without using any events now, so that
- the 'dirty' list on the new session remains unaffected.
-
- .. change::
- :tags: orm
- :tickets:
-
- fixed bug which could arise when using session.begin_nested() in conjunction
- with more than one level deep of enclosing session.begin() statements
-
- .. change::
- :tags: orm
- :tickets: 914
-
- fixed session.refresh() with instance that has custom entity_name
-
- .. change::
- :tags: dialects
- :tickets:
-
- sqlite SLDate type will not erroneously render "microseconds" portion
- of a datetime or time object.
-
- .. change::
- :tags: dialects
- :tickets: 902
-
- oracle
- - added disconnect detection support for Oracle
- - some cleanup to binary/raw types so that cx_oracle.LOB is detected
- on an ad-hoc basis
-
- .. change::
- :tags: dialects
- :tickets: 824, 839, 842, 901
-
- MSSQL
- - PyODBC no longer has a global "set nocount on".
- - Fix non-identity integer PKs on autoload
- - Better support for convert_unicode
- - Less strict date conversion for pyodbc/adodbapi
- - Schema-qualified tables / autoload
-
- .. change::
- :tags: firebird, backend
- :tickets: 410
-
- does properly reflect domains (partially fixing) and
- PassiveDefaults
-
- .. change::
- :tags: 3562, firebird, backend
- :tickets:
-
- reverted to use default poolclass (was set to SingletonThreadPool in
- 0.4.0 for test purposes)
-
- .. change::
- :tags: firebird, backend
- :tickets:
-
- map func.length() to 'char_length' (easily overridable with the UDF
- 'strlen' on old versions of Firebird)
-
-.. changelog::
- :version: 0.4.1
- :released: Sun Nov 18 2007
-
- .. change::
- :tags: sql
- :tickets:
-
- the "shortname" keyword parameter on bindparam() has been
- deprecated.
-
- .. change::
- :tags: sql
- :tickets:
-
- Added contains operator (generates a "LIKE %<other>%" clause).
-
- .. change::
- :tags: sql
- :tickets:
-
- anonymous column expressions are automatically labeled.
- e.g. select([x* 5]) produces "SELECT x * 5 AS anon_1".
- This allows the labelname to be present in the cursor.description
- which can then be appropriately matched to result-column processing
- rules. (we can't reliably use positional tracking for result-column
- matches since text() expressions may represent multiple columns).
-
- .. change::
- :tags: sql
- :tickets:
-
- operator overloading is now controlled by TypeEngine objects - the
- one built-in operator overload so far is String types overloading
- '+' to be the string concatenation operator.
- User-defined types can also define their own operator overloading
- by overriding the adapt_operator(self, op) method.
-
- .. change::
- :tags: sql
- :tickets: 819
-
- untyped bind parameters on the right side of a binary expression
- will be assigned the type of the left side of the operation, to better
- enable the appropriate bind parameter processing to take effect
-
- .. change::
- :tags: sql
- :tickets: 833
-
- Removed regular expression step from most statement compilations.
- Also fixes
-
- .. change::
- :tags: sql
- :tickets:
-
- Fixed empty (zero column) sqlite inserts, allowing inserts on
- autoincrementing single column tables.
-
- .. change::
- :tags: sql
- :tickets:
-
- Fixed expression translation of text() clauses; this repairs various
- ORM scenarios where literal text is used for SQL expressions
-
- .. change::
- :tags: sql
- :tickets:
-
- Removed ClauseParameters object; compiled.params returns a regular
- dictionary now, as well as result.last_inserted_params() /
- last_updated_params().
-
- .. change::
- :tags: sql
- :tickets:
-
- Fixed INSERT statements w.r.t. primary key columns that have
- SQL-expression based default generators on them; SQL expression
- executes inline as normal but will not trigger a "postfetch" condition
- for the column, for those DB's who provide it via cursor.lastrowid
-
- .. change::
- :tags: sql
- :tickets: 844
-
- func. objects can be pickled/unpickled
-
- .. change::
- :tags: sql
- :tickets:
-
- rewrote and simplified the system used to "target" columns across
- selectable expressions. On the SQL side this is represented by the
- "corresponding_column()" method. This method is used heavily by the ORM
- to "adapt" elements of an expression to similar, aliased expressions,
- as well as to target result set columns originally bound to a
- table or selectable to an aliased, "corresponding" expression. The new
- rewrite features completely consistent and accurate behavior.
-
- .. change::
- :tags: sql
- :tickets: 573
-
- Added a field ("info") for storing arbitrary data on schema items
-
- .. change::
- :tags: sql
- :tickets:
-
- The "properties" collection on Connections has been renamed "info" to
- match schema's writable collections. Access is still available via
- the "properties" name until 0.5.
-
- .. change::
- :tags: sql
- :tickets:
-
- fixed the close() method on Transaction when using strategy='threadlocal'
-
- .. change::
- :tags: sql
- :tickets: 853
-
- fix to compiled bind parameters to not mistakenly populate None
-
- .. change::
- :tags: sql
- :tickets:
-
- <Engine|Connection>._execute_clauseelement becomes a public method
- Connectable.execute_clauseelement
-
- .. change::
- :tags: orm
- :tickets: 843
-
- eager loading with LIMIT/OFFSET applied no longer adds the primary
- table joined to a limited subquery of itself; the eager loads now
- join directly to the subquery which also provides the primary table's
- columns to the result set. This eliminates a JOIN from all eager loads
- with LIMIT/OFFSET.
-
- .. change::
- :tags: orm
- :tickets: 802
-
- session.refresh() and session.expire() now support an additional argument
- "attribute_names", a list of individual attribute keynames to be refreshed
- or expired, allowing partial reloads of attributes on an already-loaded
- instance.
-
- .. change::
- :tags: orm
- :tickets: 767
-
- added op() operator to instrumented attributes; i.e.
- User.name.op('ilike')('%somename%')
-
- .. change::
- :tags: orm
- :tickets: 676
-
- Mapped classes may now define __eq__, __hash__, and __nonzero__ methods
- with arbitrary semantics. The orm now handles all mapped instances on
- an identity-only basis. (e.g. 'is' vs '==')
-
- .. change::
- :tags: orm
- :tickets:
-
- the "properties" accessor on Mapper is removed; it now throws an informative
- exception explaining the usage of mapper.get_property() and
- mapper.iterate_properties
-
- .. change::
- :tags: orm
- :tickets:
-
- added having() method to Query, applies HAVING to the generated statement
- in the same way as filter() appends to the WHERE clause.
-
- .. change::
- :tags: orm
- :tickets: 777
-
- The behavior of query.options() is now fully based on paths, i.e. an
- option such as eagerload_all('x.y.z.y.x') will apply eagerloading to
- only those paths, i.e. and not 'x.y.x'; eagerload('children.children')
- applies only to exactly two-levels deep, etc.
-
- .. change::
- :tags: orm
- :tickets:
-
- PickleType will compare using `==` when set up with mutable=False,
- and not the `is` operator. To use `is` or any other comparator, send
- in a custom comparison function using PickleType(comparator=my_custom_comparator).
-
- .. change::
- :tags: orm
- :tickets: 848
-
- query doesn't throw an error if you use distinct() and an order_by()
- containing UnaryExpressions (or other) together
-
- .. change::
- :tags: orm
- :tickets: 786
-
- order_by() expressions from joined tables are properly added to columns
- clause when using distinct()
-
- .. change::
- :tags: orm
- :tickets: 858
-
- fixed error where Query.add_column() would not accept a class-bound
- attribute as an argument; Query also raises an error if an invalid
- argument was sent to add_column() (at instances() time)
-
- .. change::
- :tags: orm
- :tickets:
-
- added a little more checking for garbage-collection dereferences in
- InstanceState.__cleanup() to reduce "gc ignored" errors on app
- shutdown
-
- .. change::
- :tags: orm
- :tickets:
-
- The session API has been solidified:
-
- .. change::
- :tags: orm
- :tickets: 840
-
- It's an error to session.save() an object which is already
- persistent
-
- .. change::
- :tags: orm
- :tickets:
-
- It's an error to session.delete() an object which is *not*
- persistent.
-
- .. change::
- :tags: orm
- :tickets:
-
- session.update() and session.delete() raise an error when updating
- or deleting an instance that is already in the session with a
- different identity.
-
- .. change::
- :tags: orm
- :tickets:
-
- The session checks more carefully when determining "object X already
- in another session"; e.g. if you pickle a series of objects and
- unpickle (i.e. as in a Pylons HTTP session or similar), they can go
- into a new session without any conflict
-
- .. change::
- :tags: orm
- :tickets:
-
- merge() includes a keyword argument "dont_load=True". setting this
- flag will cause the merge operation to not load any data from the
- database in response to incoming detached objects, and will accept
- the incoming detached object as though it were already present in
- that session. Use this to merge detached objects from external
- caching systems into the session.
-
- .. change::
- :tags: orm
- :tickets:
-
- Deferred column attributes no longer trigger a load operation when the
- attribute is assigned to. In those cases, the newly assigned value
- will be present in the flushes' UPDATE statement unconditionally.
-
- .. change::
- :tags: orm
- :tickets: 834
-
- Fixed a truncation error when re-assigning a subset of a collection
- (obj.relation = obj.relation[1:])
-
- .. change::
- :tags: orm
- :tickets: 832
-
- De-cruftified backref configuration code, backrefs which step on
- existing properties now raise an error
-
- .. change::
- :tags: orm
- :tickets: 831
-
- Improved behavior of add_property() etc., fixed involving
- synonym/deferred.
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed clear_mappers() behavior to better clean up after itself.
-
- .. change::
- :tags: orm
- :tickets: 841
-
- Fix to "row switch" behavior, i.e. when an INSERT/DELETE is combined
- into a single UPDATE; many-to-many relations on the parent object
- update properly.
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed __hash__ for association proxy- these collections are unhashable,
- just like their mutable Python counterparts.
-
- .. change::
- :tags: orm
- :tickets:
-
- Added proxying of save_or_update, __contains__ and __iter__ methods for
- scoped sessions.
-
- .. change::
- :tags: orm
- :tickets: 852
-
- fixed very hard-to-reproduce issue where by the FROM clause of Query
- could get polluted by certain generative calls
-
- .. change::
- :tags: dialects
- :tickets:
-
- Added experimental support for MaxDB (versions >= 7.6.03.007 only).
-
- .. change::
- :tags: dialects
- :tickets:
-
- oracle will now reflect "DATE" as an OracleDateTime column, not
- OracleDate
-
- .. change::
- :tags: dialects
- :tickets: 847
-
- added awareness of schema name in oracle table_names() function,
- fixes metadata.reflect(schema='someschema')
-
- .. change::
- :tags: dialects
- :tickets:
-
- MSSQL anonymous labels for selection of functions made deterministic
-
- .. change::
- :tags: dialects
- :tickets:
-
- sqlite will reflect "DECIMAL" as a numeric column.
-
- .. change::
- :tags: dialects
- :tickets: 828
-
- Made access dao detection more reliable
-
- .. change::
- :tags: dialects
- :tickets:
-
- Renamed the Dialect attribute 'preexecute_sequences' to
- 'preexecute_pk_sequences'. An attribute proxy is in place for
- out-of-tree dialects using the old name.
-
- .. change::
- :tags: dialects
- :tickets:
-
- Added test coverage for unknown type reflection. Fixed sqlite/mysql
- handling of type reflection for unknown types.
-
- .. change::
- :tags: dialects
- :tickets:
-
- Added REAL for mysql dialect (for folks exploiting the
- REAL_AS_FLOAT sql mode).
-
- .. change::
- :tags: dialects
- :tickets:
-
- mysql Float, MSFloat and MSDouble constructed without arguments
- now produce no-argument DDL, e.g.'FLOAT'.
-
- .. change::
- :tags: misc
- :tickets:
-
- Removed unused util.hash().
-
-.. changelog::
- :version: 0.4.0
- :released: Wed Oct 17 2007
-
- .. change::
- :tags:
- :tickets:
-
- (see 0.4.0beta1 for the start of major changes against 0.3,
- as well as https://www.sqlalchemy.org/trac/wiki/WhatsNewIn04 )
-
- .. change::
- :tags:
- :tickets: 785
-
- Added initial Sybase support (mxODBC so far)
-
- .. change::
- :tags:
- :tickets:
-
- Added partial index support for PostgreSQL. Use the postgres_where keyword
- on the Index.
-
- .. change::
- :tags:
- :tickets: 817
-
- string-based query param parsing/config file parser understands
- wider range of string values for booleans
-
- .. change::
- :tags:
- :tickets: 813
-
- backref remove object operation doesn't fail if the other-side
- collection doesn't contain the item, supports noload collections
-
- .. change::
- :tags:
- :tickets: 818
-
- removed __len__ from "dynamic" collection as it would require issuing
- a SQL "count()" operation, thus forcing all list evaluations to issue
- redundant SQL
-
- .. change::
- :tags:
- :tickets: 816
-
- inline optimizations added to locate_dirty() which can greatly speed up
- repeated calls to flush(), as occurs with autoflush=True
-
- .. change::
- :tags:
- :tickets:
-
- The IdentifierPreprarer's _requires_quotes test is now regex based. Any
- out-of-tree dialects that provide custom sets of legal_characters or
- illegal_initial_characters will need to move to regexes or override
- _requires_quotes.
-
- .. change::
- :tags:
- :tickets:
-
- Firebird has supports_sane_rowcount and supports_sane_multi_rowcount set
- to False due to ticket #370 (right way).
-
- .. change::
- :tags:
- :tickets:
-
- Improvements and fixes on Firebird reflection:
- * FBDialect now mimics OracleDialect, regarding case-sensitivity of TABLE and
- COLUMN names (see 'case_sensitive remotion' topic on this current file).
- * FBDialect.table_names() doesn't bring system tables (ticket:796).
- * FB now reflects Column's nullable property correctly.
-
- .. change::
- :tags:
- :tickets:
-
- Fixed SQL compiler's awareness of top-level column labels as used
- in result-set processing; nested selects which contain the same column
- names don't affect the result or conflict with result-column metadata.
-
- .. change::
- :tags:
- :tickets:
-
- query.get() and related functions (like many-to-one lazyloading)
- use compile-time-aliased bind parameter names, to prevent
- name conflicts with bind parameters that already exist in the
- mapped selectable.
-
- .. change::
- :tags:
- :tickets: 795
-
- Fixed three- and multi-level select and deferred inheritance loading
- (i.e. abc inheritance with no select_table).
-
- .. change::
- :tags:
- :tickets:
-
- Ident passed to id_chooser in shard.py always a list.
-
- .. change::
- :tags:
- :tickets:
-
- The no-arg ResultProxy._row_processor() is now the class attribute
- `_process_row`.
-
- .. change::
- :tags:
- :tickets: 797
-
- Added support for returning values from inserts and updates for
- PostgreSQL 8.2+.
-
- .. change::
- :tags:
- :tickets:
-
- PG reflection, upon seeing the default schema name being used explicitly
- as the "schema" argument in a Table, will assume that this is the
- user's desired convention, and will explicitly set the "schema" argument
- in foreign-key-related reflected tables, thus making them match only
- with Table constructors that also use the explicit "schema" argument
- (even though its the default schema).
- In other words, SA assumes the user is being consistent in this usage.
-
- .. change::
- :tags:
- :tickets: 808
-
- fixed sqlite reflection of BOOL/BOOLEAN
-
- .. change::
- :tags:
- :tickets:
-
- Added support for UPDATE with LIMIT on mysql.
-
- .. change::
- :tags:
- :tickets: 803
-
- null foreign key on a m2o doesn't trigger a lazyload
-
- .. change::
- :tags:
- :tickets: 800
-
- oracle does not implicitly convert to unicode for non-typed result
- sets (i.e. when no TypeEngine/String/Unicode type is even being used;
- previously it was detecting DBAPI types and converting regardless).
- should fix
-
- .. change::
- :tags:
- :tickets: 806
-
- fix to anonymous label generation of long table/column names
-
- .. change::
- :tags:
- :tickets:
-
- Firebird dialect now uses SingletonThreadPool as poolclass.
-
- .. change::
- :tags:
- :tickets:
-
- Firebird now uses dialect.preparer to format sequences names
-
- .. change::
- :tags:
- :tickets: 810
-
- Fixed breakage with postgres and multiple two-phase transactions. Two-phase
- commits and rollbacks didn't automatically end up with a new transaction
- as the usual dbapi commits/rollbacks do.
-
- .. change::
- :tags:
- :tickets:
-
- Added an option to the _ScopedExt mapper extension to not automatically
- save new objects to session on object initialization.
-
- .. change::
- :tags:
- :tickets:
-
- fixed Oracle non-ansi join syntax
-
- .. change::
- :tags:
- :tickets:
-
- PickleType and Interval types (on db not supporting it natively) are now
- slightly faster.
-
- .. change::
- :tags:
- :tickets:
-
- Added Float and Time types to Firebird (FBFloat and FBTime). Fixed
- BLOB SUB_TYPE for TEXT and Binary types.
-
- .. change::
- :tags:
- :tickets:
-
- Changed the API for the in\_ operator. in_() now accepts a single argument
- that is a sequence of values or a selectable. The old API of passing in
- values as varargs still works but is deprecated.
-
-.. changelog::
- :version: 0.4.0beta6
- :released: Thu Sep 27 2007
-
- .. change::
- :tags:
- :tickets:
-
- The Session identity map is now *weak referencing* by default, use
- weak_identity_map=False to use a regular dict. The weak dict we are using
- is customized to detect instances which are "dirty" and maintain a
- temporary strong reference to those instances until changes are flushed.
-
- .. change::
- :tags:
- :tickets: 758
-
- Mapper compilation has been reorganized such that most compilation occurs
- upon mapper construction. This allows us to have fewer calls to
- mapper.compile() and also to allow class-based properties to force a
- compilation (i.e. User.addresses == 7 will compile all mappers; this is). The only caveat here is that an inheriting mapper now
- looks for its inherited mapper upon construction; so mappers within
- inheritance relationships need to be constructed in inheritance order
- (which should be the normal case anyway).
-
- .. change::
- :tags:
- :tickets:
-
- added "FETCH" to the keywords detected by Postgres to indicate a
- result-row holding statement (i.e. in addition to "SELECT").
-
- .. change::
- :tags:
- :tickets:
-
- Added full list of SQLite reserved keywords so that they get escaped
- properly.
-
- .. change::
- :tags:
- :tickets:
-
- Tightened up the relationship between the Query's generation of "eager
- load" aliases, and Query.instances() which actually grabs the eagerly
- loaded rows. If the aliases were not specifically generated for that
- statement by EagerLoader, the EagerLoader will not take effect when the
- rows are fetched. This prevents columns from being grabbed accidentally
- as being part of an eager load when they were not meant for such, which
- can happen with textual SQL as well as some inheritance situations. It's
- particularly important since the "anonymous aliasing" of columns uses
- simple integer counts now to generate labels.
-
- .. change::
- :tags:
- :tickets:
-
- Removed "parameters" argument from clauseelement.compile(), replaced with
- "column_keys". The parameters sent to execute() only interact with the
- insert/update statement compilation process in terms of the column names
- present but not the values for those columns. Produces more consistent
- execute/executemany behavior, simplifies things a bit internally.
-
- .. change::
- :tags:
- :tickets: 560
-
- Added 'comparator' keyword argument to PickleType. By default, "mutable"
- PickleType does a "deep compare" of objects using their dumps()
- representation. But this doesn't work for dictionaries. Pickled objects
- which provide an adequate __eq__() implementation can be set up with
- "PickleType(comparator=operator.eq)"
-
- .. change::
- :tags:
- :tickets:
-
- Added session.is_modified(obj) method; performs the same "history"
- comparison operation as occurs within a flush operation; setting
- include_collections=False gives the same result as is used when the flush
- determines whether or not to issue an UPDATE for the instance's row.
-
- .. change::
- :tags:
- :tickets: 584, 761
-
- Added "schema" argument to Sequence; use this with Postgres /Oracle when
- the sequence is located in an alternate schema. Implements part of, should fix.
-
- .. change::
- :tags:
- :tickets:
-
- Fixed reflection of the empty string for mysql enums.
-
- .. change::
- :tags:
- :tickets: 794
-
- Changed MySQL dialect to use the older LIMIT <offset>, <limit> syntax
- instead of LIMIT <l> OFFSET <o> for folks using 3.23.
-
- .. change::
- :tags:
- :tickets:
-
- Added 'passive_deletes="all"' flag to relation(), disables all nulling-out
- of foreign key attributes during a flush where the parent object is
- deleted.
-
- .. change::
- :tags:
- :tickets:
-
- Column defaults and onupdates, executing inline, will add parenthesis for
- subqueries and other parenthesis-requiring expressions
-
- .. change::
- :tags:
- :tickets: 793
-
- The behavior of String/Unicode types regarding that they auto-convert to
- TEXT/CLOB when no length is present now occurs *only* for an exact type of
- String or Unicode with no arguments. If you use VARCHAR or NCHAR
- (subclasses of String/Unicode) with no length, they will be interpreted by
- the dialect as VARCHAR/NCHAR; no "magic" conversion happens there. This
- is less surprising behavior and in particular this helps Oracle keep
- string-based bind parameters as VARCHARs and not CLOBs.
-
- .. change::
- :tags:
- :tickets: 771
-
- Fixes to ShardedSession to work with deferred columns.
-
- .. change::
- :tags:
- :tickets:
-
- User-defined shard_chooser() function must accept "clause=None" argument;
- this is the ClauseElement passed to session.execute(statement) and can be
- used to determine correct shard id (since execute() doesn't take an
- instance.)
-
- .. change::
- :tags:
- :tickets: 764
-
- Adjusted operator precedence of NOT to match '==' and others, so that
- ~(x <operator> y) produces NOT (x <op> y), which is better compatible
- with older MySQL versions.. This doesn't apply to "~(x==y)"
- as it does in 0.3 since ~(x==y) compiles to "x != y", but still applies
- to operators like BETWEEN.
-
- .. change::
- :tags:
- :tickets: 757, 768, 779, 728
-
- Other tickets:,,.
-
-.. changelog::
- :version: 0.4.0beta5
- :released:
-
- .. change::
- :tags:
- :tickets: 754
-
- Connection pool fixes; the better performance of beta4 remains but fixes
- "connection overflow" and other bugs which were present (like).
-
- .. change::
- :tags:
- :tickets: 769
-
- Fixed bugs in determining proper sync clauses from custom inherit
- conditions.
-
- .. change::
- :tags:
- :tickets: 763
-
- Extended 'engine_from_config' coercion for QueuePool size / overflow.
-
- .. change::
- :tags:
- :tickets: 748
-
- mysql views can be reflected again.
-
- .. change::
- :tags:
- :tickets:
-
- AssociationProxy can now take custom getters and setters.
-
- .. change::
- :tags:
- :tickets:
-
- Fixed malfunctioning BETWEEN in orm queries.
-
- .. change::
- :tags:
- :tickets: 762
-
- Fixed OrderedProperties pickling
-
- .. change::
- :tags:
- :tickets:
-
- SQL-expression defaults and sequences now execute "inline" for all
- non-primary key columns during an INSERT or UPDATE, and for all columns
- during an executemany()-style call. inline=True flag on any insert/update
- statement also forces the same behavior with a single execute().
- result.postfetch_cols() is a collection of columns for which the previous
- single insert or update statement contained a SQL-side default expression.
-
- .. change::
- :tags:
- :tickets: 759
-
- Fixed PG executemany() behavior.
-
- .. change::
- :tags:
- :tickets:
-
- postgres reflects tables with autoincrement=False for primary key columns
- which have no defaults.
-
- .. change::
- :tags:
- :tickets:
-
- postgres no longer wraps executemany() with individual execute() calls,
- instead favoring performance. "rowcount"/"concurrency" checks with
- deleted items (which use executemany) are disabled with PG since psycopg2
- does not report proper rowcount for executemany().
-
- .. change::
- :tags: tickets, fixed
- :tickets: 742
-
-
-
- .. change::
- :tags: tickets, fixed
- :tickets: 748
-
-
-
- .. change::
- :tags: tickets, fixed
- :tickets: 760
-
-
-
- .. change::
- :tags: tickets, fixed
- :tickets: 762
-
-
-
- .. change::
- :tags: tickets, fixed
- :tickets: 763
-
-
-
-.. changelog::
- :version: 0.4.0beta4
- :released: Wed Aug 22 2007
-
- .. change::
- :tags:
- :tickets:
-
- Tidied up what ends up in your namespace when you 'from sqlalchemy import \*':
-
- .. change::
- :tags:
- :tickets:
-
- 'table' and 'column' are no longer imported. They remain available by
- direct reference (as in 'sql.table' and 'sql.column') or a glob import
- from the sql package. It was too easy to accidentally use a
- sql.expressions.table instead of schema.Table when just starting out
- with SQLAlchemy, likewise column.
-
- .. change::
- :tags:
- :tickets:
-
- Internal-ish classes like ClauseElement, FromClause, NullTypeEngine,
- etc., are also no longer imported into your namespace
-
- .. change::
- :tags:
- :tickets:
-
- The 'Smallinteger' compatibility name (small i!) is no longer imported,
- but remains in schema.py for now. SmallInteger (big I!) is still
- imported.
-
- .. change::
- :tags:
- :tickets:
-
- The connection pool uses a "threadlocal" strategy internally to return
- the same connection already bound to a thread, for "contextual" connections;
- these are the connections used when you do a "connectionless" execution
- like insert().execute(). This is like a "partial" version of the
- "threadlocal" engine strategy but without the thread-local transaction part
- of it. We're hoping it reduces connection pool overhead as well as
- database usage. However, if it proves to impact stability in a negative way,
- we'll roll it right back.
-
- .. change::
- :tags:
- :tickets:
-
- Fix to bind param processing such that "False" values (like blank strings)
- still get processed/encoded.
-
- .. change::
- :tags:
- :tickets: 752
-
- Fix to select() "generative" behavior, such that calling column(),
- select_from(), correlate(), and with_prefix() does not modify the
- original select object
-
- .. change::
- :tags:
- :tickets:
-
- Added a "legacy" adapter to types, such that user-defined TypeEngine
- and TypeDecorator classes which define convert_bind_param() and/or
- convert_result_value() will continue to function. Also supports
- calling the super() version of those methods.
-
- .. change::
- :tags:
- :tickets:
-
- Added session.prune(), trims away instances cached in a session that
- are no longer referenced elsewhere. (A utility for strong-ref
- identity maps).
-
- .. change::
- :tags:
- :tickets:
-
- Added close() method to Transaction. Closes out a transaction using
- rollback if it's the outermost transaction, otherwise just ends
- without affecting the outer transaction.
-
- .. change::
- :tags:
- :tickets:
-
- Transactional and non-transactional Session integrates better with
- bound connection; a close() will ensure that connection
- transactional state is the same as that which existed on it before
- being bound to the Session.
-
- .. change::
- :tags:
- :tickets: 735
-
- Modified SQL operator functions to be module-level operators,
- allowing SQL expressions to be pickleable.
-
- .. change::
- :tags:
- :tickets:
-
- Small adjustment to mapper class.__init__ to allow for Py2.6
- object.__init__() behavior.
-
- .. change::
- :tags:
- :tickets:
-
- Fixed 'prefix' argument for select()
-
- .. change::
- :tags:
- :tickets:
-
- Connection.begin() no longer accepts nested=True, this logic is now
- all in begin_nested().
-
- .. change::
- :tags:
- :tickets:
-
- Fixes to new "dynamic" relation loader involving cascades
-
- .. change::
- :tags: tickets, fixed
- :tickets: 735
-
-
-
- .. change::
- :tags: tickets, fixed
- :tickets: 752
-
-
-
-.. changelog::
- :version: 0.4.0beta3
- :released: Thu Aug 16 2007
-
- .. change::
- :tags:
- :tickets:
-
- SQL types optimization:
-
- .. change::
- :tags:
- :tickets:
-
- New performance tests show a combined mass-insert/mass-select test as
- having 68% fewer function calls than the same test run against 0.3.
-
- .. change::
- :tags:
- :tickets:
-
- General performance improvement of result set iteration is around 10-20%.
-
- .. change::
- :tags:
- :tickets:
-
- In types.AbstractType, convert_bind_param() and convert_result_value()
- have migrated to callable-returning bind_processor() and
- result_processor() methods. If no callable is returned, no pre/post
- processing function is called.
-
- .. change::
- :tags:
- :tickets:
-
- Hooks added throughout base/sql/defaults to optimize the calling of bind
- param/result processors so that method call overhead is minimized.
-
- .. change::
- :tags:
- :tickets:
-
- Support added for executemany() scenarios such that unneeded "last row id"
- logic doesn't kick in, parameters aren't excessively traversed.
-
- .. change::
- :tags:
- :tickets:
-
- Added 'inherit_foreign_keys' arg to mapper().
-
- .. change::
- :tags:
- :tickets:
-
- Added support for string date passthrough in sqlite.
-
- .. change::
- :tags: tickets, fixed
- :tickets: 738
-
-
-
- .. change::
- :tags: tickets, fixed
- :tickets: 739
-
-
-
- .. change::
- :tags: tickets, fixed
- :tickets: 743
-
-
-
- .. change::
- :tags: tickets, fixed
- :tickets: 744
-
-
-
-.. changelog::
- :version: 0.4.0beta2
- :released: Tue Aug 14 2007
-
- .. change::
- :tags: oracle, improvements.
- :tickets:
-
- Auto-commit after LOAD DATA INFILE for mysql.
-
- .. change::
- :tags: oracle, improvements.
- :tickets:
-
- A rudimental SessionExtension class has been added, allowing user-defined
- functionality to take place at flush(), commit(), and rollback() boundaries.
-
- .. change::
- :tags: oracle, improvements.
- :tickets:
-
- Added engine_from_config() function for helping to create_engine() from an
- .ini style config.
-
- .. change::
- :tags: oracle, improvements.
- :tickets:
-
- base_mapper() becomes a plain attribute.
-
- .. change::
- :tags: oracle, improvements.
- :tickets:
-
- session.execute() and scalar() can search for a Table with which to bind from
- using the given ClauseElement.
-
- .. change::
- :tags: oracle, improvements.
- :tickets:
-
- Session automatically extrapolates tables from mappers with binds, also uses
- base_mapper so that inheritance hierarchies bind automatically.
-
- .. change::
- :tags: oracle, improvements.
- :tickets:
-
- Moved ClauseVisitor traversal back to inlined non-recursive.
-
- .. change::
- :tags: tickets, fixed
- :tickets: 730
-
-
-
- .. change::
- :tags: tickets, fixed
- :tickets: 732
-
-
-
- .. change::
- :tags: tickets, fixed
- :tickets: 733
-
-
-
- .. change::
- :tags: tickets, fixed
- :tickets: 734
-
-
-
-.. changelog::
- :version: 0.4.0beta1
- :released: Sun Aug 12 2007
-
- .. change::
- :tags: orm
- :tickets:
-
- Speed! Along with recent speedups to ResultProxy, total number of function
- calls significantly reduced for large loads.
-
- .. change::
- :tags: orm
- :tickets:
-
- test/perf/masseagerload.py reports 0.4 as having the fewest number of
- function calls across all SA versions (0.1, 0.2, and 0.3).
-
- .. change::
- :tags: orm
- :tickets: 213
-
- New collection_class api and implementation. Collections are
- now instrumented via decorations rather than proxying. You can now have
- collections that manage their own membership, and your class instance will
- be directly exposed on the relation property. The changes are transparent
- for most users.
-
- .. change::
- :tags: orm
- :tickets:
-
- InstrumentedList (as it was) is removed, and relation properties no
- longer have 'clear()', '.data', or any other added methods beyond those
- provided by the collection type. You are free, of course, to add them to
- a custom class.
-
- .. change::
- :tags: orm
- :tickets:
-
- __setitem__-like assignments now fire remove events for the existing
- value, if any.
-
- .. change::
- :tags: orm
- :tickets:
-
- dict-likes used as collection classes no longer need to change __iter__
- semantics- itervalues() is used by default instead. This is a backwards
- incompatible change.
-
- .. change::
- :tags: orm
- :tickets:
-
- Subclassing dict for a mapped collection is no longer needed in most
- cases. orm.collections provides canned implementations that key objects
- by a specified column or a custom function of your choice.
-
- .. change::
- :tags: orm
- :tickets:
-
- Collection assignment now requires a compatible type- assigning None to
- clear a collection or assigning a list to a dict collection will now
- raise an argument error.
-
- .. change::
- :tags: orm
- :tickets:
-
- AttributeExtension moved to interfaces, and .delete is now .remove The
- event method signature has also been swapped around.
-
- .. change::
- :tags: orm
- :tickets:
-
- Major overhaul for Query:
-
- .. change::
- :tags: orm
- :tickets:
-
- All selectXXX methods are deprecated. Generative methods are now the
- standard way to do things, i.e. filter(), filter_by(), all(), one(),
- etc. Deprecated methods are docstring'ed with their new replacements.
-
- .. change::
- :tags: orm
- :tickets: 643
-
- Class-level properties are now usable as query elements... no more
- '.c.'! "Class.c.propname" is now superseded by "Class.propname". All
- clause operators are supported, as well as higher level operators such
- as Class.prop==<some instance> for scalar attributes,
- Class.prop.contains(<some instance>) and Class.prop.any(<some
- expression>) for collection-based attributes (all are also
- negatable). Table-based column expressions as well as columns mounted
- on mapped classes via 'c' are of course still fully available and can be
- freely mixed with the new attributes.
-
- .. change::
- :tags: orm
- :tickets:
-
- Removed ancient query.select_by_attributename() capability.
-
- .. change::
- :tags: orm
- :tickets:
-
- The aliasing logic used by eager loading has been generalized, so that
- it also adds full automatic aliasing support to Query. It's no longer
- necessary to create an explicit Alias to join to the same tables
- multiple times; *even for self-referential relationships*.
-
- - join() and outerjoin() take arguments "aliased=True". Yhis causes
- their joins to be built on aliased tables; subsequent calls to
- filter() and filter_by() will translate all table expressions (yes,
- real expressions using the original mapped Table) to be that of the
- Alias for the duration of that join() (i.e. until reset_joinpoint() or
- another join() is called).
-
- - join() and outerjoin() take arguments "id=<somestring>". When used
- with "aliased=True", the id can be referenced by add_entity(cls,
- id=<somestring>) so that you can select the joined instances even if
- they're from an alias.
-
- - join() and outerjoin() now work with self-referential relationships!
- Using "aliased=True", you can join as many levels deep as desired,
- i.e. query.join(['children', 'children'], aliased=True); filter
- criterion will be against the rightmost joined table
-
- .. change::
- :tags: orm
- :tickets: 660
-
- Added query.populate_existing(), marks the query to reload all
- attributes and collections of all instances touched in the query,
- including eagerly-loaded entities.
-
- .. change::
- :tags: orm
- :tickets:
-
- Added eagerload_all(), allows eagerload_all('x.y.z') to specify eager
- loading of all properties in the given path.
-
- .. change::
- :tags: orm
- :tickets:
-
- Major overhaul for Session:
-
- .. change::
- :tags: orm
- :tickets:
-
- New function which "configures" a session called "sessionmaker()". Send
- various keyword arguments to this function once, returns a new class
- which creates a Session against that stereotype.
-
- .. change::
- :tags: orm
- :tickets:
-
- SessionTransaction removed from "public" API. You now can call begin()/
- commit()/rollback() on the Session itself.
-
- .. change::
- :tags: orm
- :tickets:
-
- Session also supports SAVEPOINT transactions; call begin_nested().
-
- .. change::
- :tags: orm
- :tickets:
-
- Session supports two-phase commit behavior when vertically or
- horizontally partitioning (i.e., using more than one engine). Use
- twophase=True.
-
- .. change::
- :tags: orm
- :tickets:
-
- Session flag "transactional=True" produces a session which always places
- itself into a transaction when first used. Upon commit(), rollback() or
- close(), the transaction ends; but begins again on the next usage.
-
- .. change::
- :tags: orm
- :tickets:
-
- Session supports "autoflush=True". This issues a flush() before each
- query. Use in conjunction with transactional, and you can just
- save()/update() and then query, the new objects will be there. Use
- commit() at the end (or flush() if non-transactional) to flush remaining
- changes.
-
- .. change::
- :tags: orm
- :tickets:
-
- New scoped_session() function replaces SessionContext and assignmapper.
- Builds onto "sessionmaker()" concept to produce a class whose Session()
- construction returns the thread-local session. Or, call all Session
- methods as class methods, i.e. Session.save(foo); Session.commit().
- just like the old "objectstore" days.
-
- .. change::
- :tags: orm
- :tickets:
-
- Added new "binds" argument to Session to support configuration of
- multiple binds with sessionmaker() function.
-
- .. change::
- :tags: orm
- :tickets:
-
- A rudimental SessionExtension class has been added, allowing
- user-defined functionality to take place at flush(), commit(), and
- rollback() boundaries.
-
- .. change::
- :tags: orm
- :tickets:
-
- Query-based relation()s available with dynamic_loader(). This is a
- *writable* collection (supporting append() and remove()) which is also a
- live Query object when accessed for reads. Ideal for dealing with very
- large collections where only partial loading is desired.
-
- .. change::
- :tags: orm
- :tickets:
-
- flush()-embedded inline INSERT/UPDATE expressions. Assign any SQL
- expression, like "sometable.c.column + 1", to an instance's attribute.
- Upon flush(), the mapper detects the expression and embeds it directly in
- the INSERT or UPDATE statement; the attribute gets deferred on the
- instance so it loads the new value the next time you access it.
-
- .. change::
- :tags: orm
- :tickets: 618
-
- A rudimental sharding (horizontal scaling) system is introduced. This
- system uses a modified Session which can distribute read and write
- operations among multiple databases, based on user-defined functions
- defining the "sharding strategy". Instances and their dependents can be
- distributed and queried among multiple databases based on attribute
- values, round-robin approaches or any other user-defined
- system.
-
- .. change::
- :tags: orm
- :tickets: 659
-
- Eager loading has been enhanced to allow even more joins in more places.
- It now functions at any arbitrary depth along self-referential and
- cyclical structures. When loading cyclical structures, specify
- "join_depth" on relation() indicating how many times you'd like the table
- to join to itself; each level gets a distinct table alias. The alias
- names themselves are generated at compile time using a simple counting
- scheme now and are a lot easier on the eyes, as well as of course
- completely deterministic.
-
- .. change::
- :tags: orm
- :tickets: 211
-
- Added composite column properties. This allows you to create a type which
- is represented by more than one column, when using the ORM. Objects of
- the new type are fully functional in query expressions, comparisons,
- query.get() clauses, etc. and act as though they are regular single-column
- scalars... except they're not! Use the function composite(cls, \*columns)
- inside of the mapper's "properties" dict, and instances of cls will be
- created/mapped to a single attribute, comprised of the values corresponding
- to \*columns.
-
- .. change::
- :tags: orm
- :tickets:
-
- Improved support for custom column_property() attributes which feature
- correlated subqueries, works better with eager loading now.
-
- .. change::
- :tags: orm
- :tickets: 611
-
- Primary key "collapse" behavior; the mapper will analyze all columns in
- its given selectable for primary key "equivalence", that is, columns which
- are equivalent via foreign key relationship or via an explicit
- inherit_condition. primarily for joined-table inheritance scenarios where
- different named PK columns in inheriting tables should "collapse" into a
- single-valued (or fewer-valued) primary key. Fixes things like.
-
- .. change::
- :tags: orm
- :tickets:
-
- Joined-table inheritance will now generate the primary key columns of all
- inherited classes against the root table of the join only. This implies
- that each row in the root table is distinct to a single instance. If for
- some rare reason this is not desirable, explicit primary_key settings on
- individual mappers will override it.
-
- .. change::
- :tags: orm
- :tickets:
-
- When "polymorphic" flags are used with joined-table or single-table
- inheritance, all identity keys are generated against the root class of the
- inheritance hierarchy; this allows query.get() to work polymorphically
- using the same caching semantics as a non-polymorphic get. Note that this
- currently does not work with concrete inheritance.
-
- .. change::
- :tags: orm
- :tickets:
-
- Secondary inheritance loading: polymorphic mappers can be constructed
- *without* a select_table argument. inheriting mappers whose tables were
- not represented in the initial load will issue a second SQL query
- immediately, once per instance (i.e. not very efficient for large lists),
- in order to load the remaining columns.
-
- .. change::
- :tags: orm
- :tickets:
-
- Secondary inheritance loading can also move its second query into a
- column-level "deferred" load, via the "polymorphic_fetch" argument, which
- can be set to 'select' or 'deferred'
-
- .. change::
- :tags: orm
- :tickets: 696
-
- It's now possible to map only a subset of available selectable columns
- onto mapper properties, using include_columns/exclude_columns..
-
- .. change::
- :tags: orm
- :tickets:
-
- Added undefer_group() MapperOption, sets a set of "deferred" columns
- joined by a "group" to load as "undeferred".
-
- .. change::
- :tags: orm
- :tickets:
-
- Rewrite of the "deterministic alias name" logic to be part of the SQL
- layer, produces much simpler alias and label names more in the style of
- Hibernate
-
- .. change::
- :tags: sql
- :tickets:
-
- Speed! Clause compilation as well as the mechanics of SQL constructs have
- been streamlined and simplified to a significant degree, for a 20-30%
- improvement of the statement construction/compilation overhead of 0.3.
-
- .. change::
- :tags: sql
- :tickets:
-
- All "type" keyword arguments, such as those to bindparam(), column(),
- Column(), and func.<something>(), renamed to "type\_". Those objects still
- name their "type" attribute as "type".
-
- .. change::
- :tags: sql
- :tickets:
-
- case_sensitive=(True|False) setting removed from schema items, since
- checking this state added a lot of method call overhead and there was no
- decent reason to ever set it to False. Table and column names which are
- all lower case will be treated as case-insensitive (yes we adjust for
- Oracle's UPPERCASE style too).
-
- .. change::
- :tags: transactions
- :tickets:
-
- Added context manager (with statement) support for transactions.
-
- .. change::
- :tags: transactions
- :tickets:
-
- Added support for two phase commit, works with mysql and postgres so far.
-
- .. change::
- :tags: transactions
- :tickets:
-
- Added a subtransaction implementation that uses savepoints.
-
- .. change::
- :tags: transactions
- :tickets:
-
- Added support for savepoints.
-
- .. change::
- :tags: metadata
- :tickets:
-
- Tables can be reflected from the database en-masse without declaring
- them in advance. MetaData(engine, reflect=True) will load all tables
- present in the database, or use metadata.reflect() for finer control.
-
- .. change::
- :tags: metadata
- :tickets:
-
- DynamicMetaData has been renamed to ThreadLocalMetaData
-
- .. change::
- :tags: metadata
- :tickets:
-
- The ThreadLocalMetaData constructor now takes no arguments.
-
- .. change::
- :tags: metadata
- :tickets:
-
- BoundMetaData has been removed- regular MetaData is equivalent
-
- .. change::
- :tags: metadata
- :tickets: 646
-
- Numeric and Float types now have an "asdecimal" flag; defaults to True for
- Numeric, False for Float. When True, values are returned as
- decimal.Decimal objects; when False, values are returned as float(). The
- defaults of True/False are already the behavior for PG and MySQL's DBAPI
- modules.
-
- .. change::
- :tags: metadata
- :tickets: 475
-
- New SQL operator implementation which removes all hardcoded operators from
- expression structures and moves them into compilation; allows greater
- flexibility of operator compilation; for example, "+" compiles to "||"
- when used in a string context, or "concat(a,b)" on MySQL; whereas in a
- numeric context it compiles to "+". Fixes.
-
- .. change::
- :tags: metadata
- :tickets:
-
- "Anonymous" alias and label names are now generated at SQL compilation
- time in a completely deterministic fashion... no more random hex IDs
-
- .. change::
- :tags: metadata
- :tickets:
-
- Significant architectural overhaul to SQL elements (ClauseElement). All
- elements share a common "mutability" framework which allows a consistent
- approach to in-place modifications of elements as well as generative
- behavior. Improves stability of the ORM which makes heavy usage of
- mutations to SQL expressions.
-
- .. change::
- :tags: metadata
- :tickets:
-
- select() and union()'s now have "generative" behavior. Methods like
- order_by() and group_by() return a *new* instance - the original instance
- is left unchanged. Non-generative methods remain as well.
-
- .. change::
- :tags: metadata
- :tickets: 569, 52
-
- The internals of select/union vastly simplified- all decision making
- regarding "is subquery" and "correlation" pushed to SQL generation phase.
- select() elements are now *never* mutated by their enclosing containers or
- by any dialect's compilation process
-
- .. change::
- :tags: metadata
- :tickets:
-
- select(scalar=True) argument is deprecated; use select(..).as_scalar().
- The resulting object obeys the full "column" interface and plays better
- within expressions.
-
- .. change::
- :tags: metadata
- :tickets: 504
-
- Added select().with_prefix('foo') allowing any set of keywords to be
- placed before the columns clause of the SELECT
-
- .. change::
- :tags: metadata
- :tickets: 686
-
- Added array slice support to row[<index>]
-
- .. change::
- :tags: metadata
- :tickets:
-
- Result sets make a better attempt at matching the DBAPI types present in
- cursor.description to the TypeEngine objects defined by the dialect, which
- are then used for result-processing. Note this only takes effect for
- textual SQL; constructed SQL statements always have an explicit type map.
-
- .. change::
- :tags: metadata
- :tickets:
-
- Result sets from CRUD operations close their underlying cursor immediately
- and will also autoclose the connection if defined for the operation; this
- allows more efficient usage of connections for successive CRUD operations
- with less chance of "dangling connections".
-
- .. change::
- :tags: metadata
- :tickets: 559
-
- Column defaults and onupdate Python functions (i.e. passed to
- ColumnDefault) may take zero or one arguments; the one argument is the
- ExecutionContext, from which you can call "context.parameters[someparam]"
- to access the other bind parameter values affixed to the statement. The connection used for the execution is available as well
- so that you can pre-execute statements.
-
- .. change::
- :tags: metadata
- :tickets:
-
- Added "explicit" create/drop/execute support for sequences (i.e. you can
- pass a "connectable" to each of those methods on Sequence).
-
- .. change::
- :tags: metadata
- :tickets:
-
- Better quoting of identifiers when manipulating schemas.
-
- .. change::
- :tags: metadata
- :tickets:
-
- Standardized the behavior for table reflection where types can't be
- located; NullType is substituted instead, warning is raised.
-
- .. change::
- :tags: metadata
- :tickets: 606
-
- ColumnCollection (i.e. the 'c' attribute on tables) follows dictionary
- semantics for "__contains__"
-
- .. change::
- :tags: engines
- :tickets:
-
- Speed! The mechanics of result processing and bind parameter processing
- have been overhauled, streamlined and optimized to issue as little method
- calls as possible. Bench tests for mass INSERT and mass rowset iteration
- both show 0.4 to be over twice as fast as 0.3, using 68% fewer function
- calls.
-
- .. change::
- :tags: engines
- :tickets:
-
- You can now hook into the pool lifecycle and run SQL statements or other
- logic at new each DBAPI connection, pool check-out and check-in.
-
- .. change::
- :tags: engines
- :tickets:
-
- Connections gain a .properties collection, with contents scoped to the
- lifetime of the underlying DBAPI connection
-
- .. change::
- :tags: engines
- :tickets:
-
- Removed auto_close_cursors and disallow_open_cursors arguments from Pool;
- reduces overhead as cursors are normally closed by ResultProxy and
- Connection.
-
- .. change::
- :tags: extensions
- :tickets:
-
- proxyengine is temporarily removed, pending an actually working
- replacement.
-
- .. change::
- :tags: extensions
- :tickets:
-
- SelectResults has been replaced by Query. SelectResults /
- SelectResultsExt still exist but just return a slightly modified Query
- object for backwards-compatibility. join_to() method from SelectResults
- isn't present anymore, need to use join().
-
- .. change::
- :tags: mysql
- :tickets:
-
- Table and column names loaded via reflection are now Unicode.
-
- .. change::
- :tags: mysql
- :tickets:
-
- All standard column types are now supported, including SET.
-
- .. change::
- :tags: mysql
- :tickets:
-
- Table reflection can now be performed in as little as one round-trip.
-
- .. change::
- :tags: mysql
- :tickets:
-
- ANSI and ANSI_QUOTES sql modes are now supported.
-
- .. change::
- :tags: mysql
- :tickets:
-
- Indexes are now reflected.
-
- .. change::
- :tags: postgres
- :tickets:
-
- Added PGArray datatype for using postgres array datatypes.
-
- .. change::
- :tags: oracle
- :tickets: 507
-
- Very rudimental support for OUT parameters added; use sql.outparam(name,
- type) to set up an OUT parameter, just like bindparam(); after execution,
- values are available via result.out_parameters dictionary.
+++ /dev/null
-
-=============
-0.5 Changelog
-=============
-
-
-.. changelog::
- :version: 0.5.9
- :released:
-
- .. change::
- :tags: sql
- :tickets: 1661
-
- Fixed erroneous self_group() call in expression package.
-
-.. changelog::
- :version: 0.5.8
- :released: Sat Jan 16 2010
-
- .. change::
- :tags: sql
- :tickets:
-
- The copy() method on Column now supports uninitialized,
- unnamed Column objects. This allows easy creation of
- declarative helpers which place common columns on multiple
- subclasses.
-
- .. change::
- :tags: sql
- :tickets:
-
- Default generators like Sequence() translate correctly
- across a copy() operation.
-
- .. change::
- :tags: sql
- :tickets:
-
- Sequence() and other DefaultGenerator objects are accepted
- as the value for the "default" and "onupdate" keyword
- arguments of Column, in addition to being accepted
- positionally.
-
- .. change::
- :tags: sql
- :tickets: 1568, 1617
-
- Fixed a column arithmetic bug that affected column
- correspondence for cloned selectables which contain
- free-standing column expressions. This bug is
- generally only noticeable when exercising newer
- ORM behavior only available in 0.6 via,
- but is more correct at the SQL expression level
- as well.
-
- .. change::
- :tags: postgresql
- :tickets: 1647
-
- The extract() function, which was slightly improved in
- 0.5.7, needed a lot more work to generate the correct
- typecast (the typecasts appear to be necessary in PG's
- EXTRACT quite a lot of the time). The typecast is
- now generated using a rule dictionary based
- on PG's documentation for date/time/interval arithmetic.
- It also accepts text() constructs again, which was broken
- in 0.5.7.
-
- .. change::
- :tags: firebird
- :tickets: 1646
-
- Recognize more errors as disconnections.
-
-.. changelog::
- :version: 0.5.7
- :released: Sat Dec 26 2009
-
- .. change::
- :tags: orm
- :tickets: 1543
-
- contains_eager() now works with the automatically
- generated subquery that results when you say
- "query(Parent).join(Parent.somejoinedsubclass)", i.e.
- when Parent joins to a joined-table-inheritance subclass.
- Previously contains_eager() would erroneously add the
- subclass table to the query separately producing a
- cartesian product. An example is in the ticket
- description.
-
- .. change::
- :tags: orm
- :tickets: 1553
-
- query.options() now only propagate to loaded objects
- for potential further sub-loads only for options where
- such behavior is relevant, keeping
- various unserializable options like those generated
- by contains_eager() out of individual instance states.
-
- .. change::
- :tags: orm
- :tickets: 1054
-
- Session.execute() now locates table- and
- mapper-specific binds based on a passed
- in expression which is an insert()/update()/delete()
- construct.
-
- .. change::
- :tags: orm
- :tickets:
-
- Session.merge() now properly overwrites a many-to-one or
- uselist=False attribute to None if the attribute
- is also None in the given object to be merged.
-
- .. change::
- :tags: orm
- :tickets: 1618
-
- Fixed a needless select which would occur when merging
- transient objects that contained a null primary key
- identifier.
-
- .. change::
- :tags: orm
- :tickets: 1585
-
- Mutable collection passed to the "extension" attribute
- of relation(), column_property() etc. will not be mutated
- or shared among multiple instrumentation calls, preventing
- duplicate extensions, such as backref populators,
- from being inserted into the list.
-
- .. change::
- :tags: orm
- :tickets: 1504
-
- Fixed the call to get_committed_value() on CompositeProperty.
-
- .. change::
- :tags: orm
- :tickets: 1602
-
- Fixed bug where Query would crash if a join() with no clear
- "left" side were called when a non-mapped column entity
- appeared in the columns list.
-
- .. change::
- :tags: orm
- :tickets: 1616, 1480
-
- Fixed bug whereby composite columns wouldn't load properly
- when configured on a joined-table subclass, introduced in
- version 0.5.6 as a result of the fix for. thx to Scott Torborg.
-
- .. change::
- :tags: orm
- :tickets: 1556
-
- The "use get" behavior of many-to-one relations, i.e. that a
- lazy load will fallback to the possibly cached query.get()
- value, now works across join conditions where the two compared
- types are not exactly the same class, but share the same
- "affinity" - i.e. Integer and SmallInteger. Also allows
- combinations of reflected and non-reflected types to work
- with 0.5 style type reflection, such as PGText/Text (note 0.6
- reflects types as their generic versions).
-
- .. change::
- :tags: orm
- :tickets: 1436
-
- Fixed bug in query.update() when passing Cls.attribute
- as keys in the value dict and using synchronize_session='expire'
- ('fetch' in 0.6).
-
- .. change::
- :tags: sql
- :tickets: 1603
-
- Fixed bug in two-phase transaction whereby commit() method
- didn't set the full state which allows subsequent close()
- call to succeed.
-
- .. change::
- :tags: sql
- :tickets:
-
- Fixed the "numeric" paramstyle, which apparently is the
- default paramstyle used by Informixdb.
-
- .. change::
- :tags: sql
- :tickets: 1574
-
- Repeat expressions in the columns clause of a select
- are deduped based on the identity of each clause element,
- not the actual string. This allows positional
- elements to render correctly even if they all render
- identically, such as "qmark" style bind parameters.
-
- .. change::
- :tags: sql
- :tickets: 1632
-
- The cursor associated with connection pool connections
- (i.e. _CursorFairy) now proxies `__iter__()` to the
- underlying cursor correctly.
-
- .. change::
- :tags: sql
- :tickets: 1556
-
- types now support an "affinity comparison" operation, i.e.
- that an Integer/SmallInteger are "compatible", or
- a Text/String, PickleType/Binary, etc. Part of.
-
- .. change::
- :tags: sql
- :tickets: 1641
-
- Fixed bug preventing alias() of an alias() from being
- cloned or adapted (occurs frequently in ORM operations).
-
- .. change::
- :tags: sqlite
- :tickets: 1439
-
- sqlite dialect properly generates CREATE INDEX for a table
- that is in an alternate schema.
-
- .. change::
- :tags: postgresql
- :tickets: 1085
-
- Added support for reflecting the DOUBLE PRECISION type,
- via a new postgres.PGDoublePrecision object.
- This is postgresql.DOUBLE_PRECISION in 0.6.
-
- .. change::
- :tags: postgresql
- :tickets: 460
-
- Added support for reflecting the INTERVAL YEAR TO MONTH
- and INTERVAL DAY TO SECOND syntaxes of the INTERVAL
- type.
-
- .. change::
- :tags: postgresql
- :tickets: 1576
-
- Corrected the "has_sequence" query to take current schema,
- or explicit sequence-stated schema, into account.
-
- .. change::
- :tags: postgresql
- :tickets: 1611
-
- Fixed the behavior of extract() to apply operator
- precedence rules to the "::" operator when applying
- the "timestamp" cast - ensures proper parenthesization.
-
- .. change::
- :tags: mssql
- :tickets: 1561
-
- Changed the name of TrustedConnection to
- Trusted_Connection when constructing pyodbc connect
- arguments
-
- .. change::
- :tags: oracle
- :tickets: 1637
-
- The "table_names" dialect function, used by MetaData
- .reflect(), omits "index overflow tables", a system
- table generated by Oracle when "index only tables"
- with overflow are used. These tables aren't accessible
- via SQL and can't be reflected.
-
- .. change::
- :tags: ext
- :tickets: 1570, 1523
-
- A column can be added to a joined-table declarative
- superclass after the class has been constructed
- (i.e. via class-level attribute assignment), and
- the column will be propagated down to
- subclasses. This is the reverse
- situation as that of, fixed in 0.5.6.
-
- .. change::
- :tags: ext
- :tickets: 1491
-
- Fixed a slight inaccuracy in the sharding example.
- Comparing equivalence of columns in the ORM is best
- accomplished using col1.shares_lineage(col2).
-
- .. change::
- :tags: ext
- :tickets: 1606
-
- Removed unused `load()` method from ShardedQuery.
-
-.. changelog::
- :version: 0.5.6
- :released: Sat Sep 12 2009
-
- .. change::
- :tags: orm
- :tickets: 1300
-
- Fixed bug whereby inheritance discriminator part of a
- composite primary key would fail on updates.
- Continuation of.
-
- .. change::
- :tags: orm
- :tickets: 1507
-
- Fixed bug which disallowed one side of a many-to-many
- bidirectional reference to declare itself as "viewonly"
-
- .. change::
- :tags: orm
- :tickets: 1526
-
- Added an assertion that prevents a @validates function
- or other AttributeExtension from loading an unloaded
- collection such that internal state may be corrupted.
-
- .. change::
- :tags: orm
- :tickets: 1519
-
- Fixed bug which prevented two entities from mutually
- replacing each other's primary key values within a single
- flush() for some orderings of operations.
-
- .. change::
- :tags: orm
- :tickets: 1485
-
- Fixed an obscure issue whereby a joined-table subclass
- with a self-referential eager load on the base class
- would populate the related object's "subclass" table with
- data from the "subclass" table of the parent.
-
- .. change::
- :tags: orm
- :tickets: 1477
-
- relations() now have greater ability to be "overridden",
- meaning a subclass that explicitly specifies a relation()
- overriding that of the parent class will be honored
- during a flush. This is currently to support
- many-to-many relations from concrete inheritance setups.
- Outside of that use case, YMMV.
-
- .. change::
- :tags: orm
- :tickets: 1483
-
- Squeezed a few more unnecessary "lazy loads" out of
- relation(). When a collection is mutated, many-to-one
- backrefs on the other side will not fire off to load
- the "old" value, unless "single_parent=True" is set.
- A direct assignment of a many-to-one still loads
- the "old" value in order to update backref collections
- on that value, which may be present in the session
- already, thus maintaining the 0.5 behavioral contract.
-
- .. change::
- :tags: orm
- :tickets: 1480
-
- Fixed bug whereby a load/refresh of joined table
- inheritance attributes which were based on
- column_property() or similar would fail to evaluate.
-
- .. change::
- :tags: orm
- :tickets: 1488
-
- Improved support for MapperProperty objects overriding
- that of an inherited mapper for non-concrete
- inheritance setups - attribute extensions won't randomly
- collide with each other.
-
- .. change::
- :tags: orm
- :tickets: 1487
-
- UPDATE and DELETE do not support ORDER BY, LIMIT, OFFSET,
- etc. in standard SQL. Query.update() and Query.delete()
- now raise an exception if any of limit(), offset(),
- order_by(), group_by(), or distinct() have been
- called.
-
- .. change::
- :tags: orm
- :tickets:
-
- Added AttributeExtension to sqlalchemy.orm.__all__
-
- .. change::
- :tags: orm
- :tickets: 1476
-
- Improved error message when query() is called with
- a non-SQL /entity expression.
-
- .. change::
- :tags: orm
- :tickets: 1440
-
- Using False or 0 as a polymorphic discriminator now
- works on the base class as well as a subclass.
-
- .. change::
- :tags: orm
- :tickets: 1424
-
- Added enable_assertions(False) to Query which disables
- the usual assertions for expected state - used
- by Query subclasses to engineer custom state.. See
- https://www.sqlalchemy.org/trac/wiki/UsageRecipes/PreFilteredQuery
- for an example.
-
- .. change::
- :tags: orm
- :tickets: 1501
-
- Fixed recursion issue which occurred if a mapped object's
- `__len__()` or `__nonzero__()` method resulted in state
- changes.
-
- .. change::
- :tags: orm
- :tickets: 1506
-
- Fixed incorrect exception raise in
- Weak/StrongIdentityMap.add()
-
- .. change::
- :tags: orm
- :tickets: 1522
-
- Fixed the error message for "could not find a FROM clause"
- in query.join() which would fail to issue correctly
- if the query was against a pure SQL construct.
-
- .. change::
- :tags: orm
- :tickets: 1486
-
- Fixed a somewhat hypothetical issue which would result
- in the wrong primary key being calculated for a mapper
- using the old polymorphic_union function - but this
- is old stuff.
-
- .. change::
- :tags: sql
- :tickets: 1373
-
- Fixed column.copy() to copy defaults and onupdates.
-
- .. change::
- :tags: sql
- :tickets:
-
- Fixed a bug in extract() introduced in 0.5.4 whereby
- the string "field" argument was getting treated as a
- ClauseElement, causing various errors within more
- complex SQL transformations.
-
- .. change::
- :tags: sql
- :tickets: 1420
-
- Unary expressions such as DISTINCT propagate their
- type handling to result sets, allowing conversions like
- unicode and such to take place.
-
- .. change::
- :tags: sql
- :tickets: 1482
-
- Fixed bug in Table and Column whereby passing empty
- dict for "info" argument would raise an exception.
-
- .. change::
- :tags: oracle
- :tickets: 1309
-
- Backported 0.6 fix for Oracle alias names not getting
- truncated.
-
- .. change::
- :tags: ext
- :tickets: 1446
-
- The collection proxies produced by associationproxy are now
- pickleable. A user-defined proxy_factory however
- is still not pickleable unless it defines __getstate__
- and __setstate__.
-
- .. change::
- :tags: ext
- :tickets: 1468
-
- Declarative will raise an informative exception if
- __table_args__ is passed as a tuple with no dict argument.
- Improved documentation.
-
- .. change::
- :tags: ext
- :tickets: 1527
-
- Table objects declared in the MetaData can now be used
- in string expressions sent to primaryjoin/secondaryjoin/
- secondary - the name is pulled from the MetaData of the
- declarative base.
-
- .. change::
- :tags: ext
- :tickets: 1523
-
- A column can be added to a joined-table subclass after
- the class has been constructed (i.e. via class-level
- attribute assignment). The column is added to the underlying
- Table as always, but now the mapper will rebuild its
- "join" to include the new column, instead of raising
- an error about "no such column, use column_property()
- instead".
-
- .. change::
- :tags: test
- :tickets:
-
- Added examples into the test suite so they get exercised
- regularly and cleaned up a couple deprecation warnings.
-
-.. changelog::
- :version: 0.5.5
- :released: Mon Jul 13 2009
-
- .. change::
- :tags: general
- :tickets: 970
-
- unit tests have been migrated from unittest to nose. See
- README.unittests for information on how to run the tests.
-
- .. change::
- :tags: orm
- :tickets:
-
- The "foreign_keys" argument of relation() will now propagate
- automatically to the backref in the same way that primaryjoin
- and secondaryjoin do. For the extremely rare use case where
- the backref of a relation() has intentionally different
- "foreign_keys" configured, both sides now need to be
- configured explicitly (if they do in fact require this setting,
- see the next note...).
-
- .. change::
- :tags: orm
- :tickets:
-
- ...the only known (and really, really rare) use case where a
- different foreign_keys setting was used on the
- forwards/backwards side, a composite foreign key that
- partially points to its own columns, has been enhanced such
- that the fk->itself aspect of the relation won't be used to
- determine relation direction.
-
- .. change::
- :tags: orm
- :tickets:
-
- Session.mapper is now *deprecated*.
-
- Call session.add() if you'd like a free-standing object to be
- part of your session. Otherwise, a DIY version of
- Session.mapper is now documented at
- https://www.sqlalchemy.org/trac/wiki/UsageRecipes/SessionAwareMapper
- The method will remain deprecated throughout 0.6.
-
- .. change::
- :tags: orm
- :tickets: 1431
-
- Fixed Query being able to join() from individual columns of a
- joined-table subclass entity, i.e. query(SubClass.foo,
- SubClass.bar).join(<anything>). In most cases, an error
- "Could not find a FROM clause to join from" would be
- raised. In a few others, the result would be returned in terms
- of the base class rather than the subclass - so applications
- which relied on this erroneous result need to be
- adjusted.
-
- .. change::
- :tags: orm
- :tickets: 1461
-
- Fixed a bug involving contains_eager(), which would apply
- itself to a secondary (i.e. lazy) load in a particular rare
- case, producing cartesian products. improved the targeting of
- query.options() on secondary loads overall.
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed bug introduced in 0.5.4 whereby Composite types fail
- when default-holding columns are flushed.
-
- .. change::
- :tags: orm
- :tickets: 1426
-
- Fixed another 0.5.4 bug whereby mutable attributes
- (i.e. PickleType) wouldn't be deserialized correctly when the
- whole object was serialized.
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed bug whereby session.is_modified() would raise an
- exception if any synonyms were in use.
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed potential memory leak whereby previously pickled objects
- placed back in a session would not be fully garbage collected
- unless the Session were explicitly closed out.
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed bug whereby list-based attributes, like pickletype and
- PGArray, failed to be merged() properly.
-
- .. change::
- :tags: orm
- :tickets:
-
- Repaired non-working attributes.set_committed_value function.
-
- .. change::
- :tags: orm
- :tickets:
-
- Trimmed the pickle format for InstanceState which should
- further reduce the memory footprint of pickled instances. The
- format should be backwards compatible with that of 0.5.4 and
- previous.
-
- .. change::
- :tags: orm
- :tickets: 1463
-
- sqlalchemy.orm.join and sqlalchemy.orm.outerjoin are now
- added to __all__ in sqlalchemy.orm.*.
-
- .. change::
- :tags: orm
- :tickets: 1458
-
- Fixed bug where Query exception raise would fail when
- a too-short composite primary key value were passed to
- get().
-
- .. change::
- :tags: sql
- :tickets:
-
- Removed an obscure feature of execute() (including connection,
- engine, Session) whereby a bindparam() construct can be sent
- as a key to the params dictionary. This usage is undocumented
- and is at the core of an issue whereby the bindparam() object
- created implicitly by a text() construct may have the same
- hash value as a string placed in the params dictionary and may
- result in an inappropriate match when computing the final bind
- parameters. Internal checks for this condition would add
- significant latency to the critical task of parameter
- rendering, so the behavior is removed. This is a backwards
- incompatible change for any application that may have been
- using this feature, however the feature has never been
- documented.
-
- .. change::
- :tags: engine/pool
- :tickets:
-
- Implemented recreate() for StaticPool.
-
-.. changelog::
- :version: 0.5.4p2
- :released: Tue May 26 2009
-
- .. change::
- :tags: sql
- :tickets:
-
- Repaired the printing of SQL exceptions which are not
- based on parameters or are not executemany() style.
-
- .. change::
- :tags: postgresql
- :tickets:
-
- Deprecated the hardcoded TIMESTAMP function, which when
- used as func.TIMESTAMP(value) would render "TIMESTAMP value".
- This breaks on some platforms as PostgreSQL doesn't allow
- bind parameters to be used in this context. The hard-coded
- uppercase is also inappropriate and there's lots of other
- PG casts that we'd need to support. So instead, use
- text constructs i.e. select(["timestamp '12/05/09'"]).
-
-.. changelog::
- :version: 0.5.4p1
- :released: Mon May 18 2009
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed an attribute error introduced in 0.5.4 which would
- occur when merge() was used with an incomplete object.
-
-.. changelog::
- :version: 0.5.4
- :released: Sun May 17 2009
-
- .. change::
- :tags: orm
- :tickets: 1398
-
- Significant performance enhancements regarding Sessions/flush()
- in conjunction with large mapper graphs, large numbers of
- objects:
-
- - Removed all* O(N) scanning behavior from the flush() process,
- i.e. operations that were scanning the full session,
- including an extremely expensive one that was erroneously
- assuming primary key values were changing when this
- was not the case.
-
- * one edge case remains which may invoke a full scan,
- if an existing primary key attribute is modified
- to a new value.
-
- - The Session's "weak referencing" behavior is now *full* -
- no strong references whatsoever are made to a mapped object
- or related items/collections in its __dict__. Backrefs and
- other cycles in objects no longer affect the Session's ability
- to lose all references to unmodified objects. Objects with
- pending changes still are maintained strongly until flush.
-
-
- The implementation also improves performance by moving
- the "resurrection" process of garbage collected items
- to only be relevant for mappings that map "mutable"
- attributes (i.e. PickleType, composite attrs). This removes
- overhead from the gc process and simplifies internal
- behavior.
-
- If a "mutable" attribute change is the sole change on an object
- which is then dereferenced, the mapper will not have access to
- other attribute state when the UPDATE is issued. This may present
- itself differently to some MapperExtensions.
-
- The change also affects the internal attribute API, but not
- the AttributeExtension interface nor any of the publicly
- documented attribute functions.
-
- - The unit of work no longer generates a graph of "dependency"
- processors for the full graph of mappers during flush(), instead
- creating such processors only for those mappers which represent
- objects with pending changes. This saves a tremendous number
- of method calls in the context of a large interconnected
- graph of mappers.
-
- - Cached a wasteful "table sort" operation that previously
- occurred multiple times per flush, also removing significant
- method call count from flush().
-
- - Other redundant behaviors have been simplified in
- mapper._save_obj().
-
- .. change::
- :tags: orm
- :tickets:
-
- Modified query_cls on DynamicAttributeImpl to accept a full
- mixin version of the AppenderQuery, which allows subclassing
- the AppenderMixin.
-
- .. change::
- :tags: orm
- :tickets: 1300
-
- The "polymorphic discriminator" column may be part of a
- primary key, and it will be populated with the correct
- discriminator value.
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed the evaluator not being able to evaluate IS NULL clauses.
-
- .. change::
- :tags: orm
- :tickets: 1352
-
- Fixed the "set collection" function on "dynamic" relations to
- initiate events correctly. Previously a collection could only
- be assigned to a pending parent instance, otherwise modified
- events would not be fired correctly. Set collection is now
- compatible with merge(), fixes.
-
- .. change::
- :tags: orm
- :tickets:
-
- Allowed pickling of PropertyOption objects constructed with
- instrumented descriptors; previously, pickle errors would occur
- when pickling an object which was loaded with a descriptor-based
- option, such as query.options(eagerload(MyClass.foo)).
-
- .. change::
- :tags: orm
- :tickets: 1357
-
- Lazy loader will not use get() if the "lazy load" SQL clause
- matches the clause used by get(), but contains some parameters
- hardcoded. Previously the lazy strategy would fail with the
- get(). Ideally get() would be used with the hardcoded
- parameters but this would require further development.
-
- .. change::
- :tags: orm
- :tickets: 1391
-
- MapperOptions and other state associated with query.options()
- is no longer bundled within callables associated with each
- lazy/deferred-loading attribute during a load.
- The options are now associated with the instance's
- state object just once when it's populated. This removes
- the need in most cases for per-instance/attribute loader
- objects, improving load speed and memory overhead for
- individual instances.
-
- .. change::
- :tags: orm
- :tickets: 1360
-
- Fixed another location where autoflush was interfering
- with session.merge(). autoflush is disabled completely
- for the duration of merge() now.
-
- .. change::
- :tags: orm
- :tickets: 1406
-
- Fixed bug which prevented "mutable primary key" dependency
- logic from functioning properly on a one-to-one
- relation().
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed bug in relation(), introduced in 0.5.3,
- whereby a self referential relation
- from a base class to a joined-table subclass would
- not configure correctly.
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed obscure mapper compilation issue when inheriting
- mappers are used which would result in un-initialized
- attributes.
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed documentation for session weak_identity_map -
- the default value is True, indicating a weak
- referencing map in use.
-
- .. change::
- :tags: orm
- :tickets: 1376
-
- Fixed a unit of work issue whereby the foreign
- key attribute on an item contained within a collection
- owned by an object being deleted would not be set to
- None if the relation() was self-referential.
-
- .. change::
- :tags: orm
- :tickets: 1378
-
- Fixed Query.update() and Query.delete() failures with eagerloaded
- relations.
-
- .. change::
- :tags: orm
- :tickets:
-
- It is now an error to specify both columns of a binary primaryjoin
- condition in the foreign_keys or remote_side collection. Whereas
- previously it was just nonsensical, but would succeed in a
- non-deterministic way.
-
- .. change::
- :tags: ticket: 594, 1341, schema
- :tickets:
-
- Added a quote_schema() method to the IdentifierPreparer class
- so that dialects can override how schemas get handled. This
- enables the MSSQL dialect to treat schemas as multipart
- identifiers, such as 'database.owner'.
-
- .. change::
- :tags: sql
- :tickets:
-
- Back-ported the "compiler" extension from SQLA 0.6. This
- is a standardized interface which allows the creation of custom
- ClauseElement subclasses and compilers. In particular it's
- handy as an alternative to text() when you'd like to
- build a construct that has database-specific compilations.
- See the extension docs for details.
-
- .. change::
- :tags: sql
- :tickets: 1413
-
- Exception messages are truncated when the list of bound
- parameters is larger than 10, preventing enormous
- multi-page exceptions from filling up screens and logfiles
- for large executemany() statements.
-
- .. change::
- :tags: sql
- :tickets:
-
- ``sqlalchemy.extract()`` is now dialect sensitive and can
- extract components of timestamps idiomatically across the
- supported databases, including SQLite.
-
- .. change::
- :tags: sql
- :tickets: 1353
-
- Fixed __repr__() and other _get_colspec() methods on
- ForeignKey constructed from __clause_element__() style
- construct (i.e. declarative columns).
-
- .. change::
- :tags: mysql
- :tickets: 1405
-
- Reflecting a FOREIGN KEY construct will take into account
- a dotted schema.tablename combination, if the foreign key
- references a table in a remote schema.
-
- .. change::
- :tags: mssql
- :tickets:
-
- Modified how savepoint logic works to prevent it from
- stepping on non-savepoint oriented routines. Savepoint
- support is still very experimental.
-
- .. change::
- :tags: mssql
- :tickets: 1310
-
- Added in reserved words for MSSQL that covers version 2008
- and all prior versions.
-
- .. change::
- :tags: mssql
- :tickets: 1343
-
- Corrected problem with information schema not working with a
- binary collation based database. Cleaned up information schema
- since it is only used by mssql now.
-
- .. change::
- :tags: sqlite
- :tickets: 1402
-
- Corrected the SLBoolean type so that it properly treats only 1
- as True.
-
- .. change::
- :tags: sqlite
- :tickets: 1273
-
- Corrected the float type so that it correctly maps to a
- SLFloat type when being reflected.
-
- .. change::
- :tags: extensions
- :tickets: 1379
-
- Fixed adding of deferred or other column properties to a
- declarative class.
-
-.. changelog::
- :version: 0.5.3
- :released: Tue Mar 24 2009
-
- .. change::
- :tags: orm
- :tickets: 1315
-
- The "objects" argument to session.flush() is deprecated.
- State which represents the linkage between a parent and
- child object does not support "flushed" status on
- one side of the link and not the other, so supporting
- this operation leads to misleading results.
-
- .. change::
- :tags: orm
- :tickets:
-
- Query now implements __clause_element__() which produces
- its selectable, which means a Query instance can be accepted
- in many SQL expressions, including col.in_(query),
- union(query1, query2), select([foo]).select_from(query),
- etc.
-
- .. change::
- :tags: orm
- :tickets: 1337
-
- Query.join() can now construct multiple FROM clauses, if
- needed. Such as, query(A, B).join(A.x).join(B.y)
- might say SELECT A.*, B.* FROM A JOIN X, B JOIN Y.
- Eager loading can also tack its joins onto those
- multiple FROM clauses.
-
- .. change::
- :tags: orm
- :tickets: 1347
-
- Fixed bug in dynamic_loader() where append/remove events
- after construction time were not being propagated to the
- UOW to pick up on flush().
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed bug where column_prefix wasn't being checked before
- not mapping an attribute that already had class-level
- name present.
-
- .. change::
- :tags: orm
- :tickets: 1315
-
- a session.expire() on a particular collection attribute
- will clear any pending backref additions as well, so that
- the next access correctly returns only what was present
- in the database. Presents some degree of a workaround for, although we are considering removing the
- flush([objects]) feature altogether.
-
- .. change::
- :tags: orm
- :tickets:
-
- Session.scalar() now converts raw SQL strings to text()
- the same way Session.execute() does and accepts same
- alternative \**kw args.
-
- .. change::
- :tags: orm
- :tickets:
-
- improvements to the "determine direction" logic of
- relation() such that the direction of tricky situations
- like mapper(A.join(B)) -> relation-> mapper(B) can be
- determined.
-
- .. change::
- :tags: orm
- :tickets: 1306
-
- When flushing partial sets of objects using session.flush([somelist]),
- pending objects which remain pending after the operation won't
- inadvertently be added as persistent.
-
- .. change::
- :tags: orm
- :tickets: 1314
-
- Added "post_configure_attribute" method to InstrumentationManager,
- so that the "listen_for_events.py" example works again.
-
- .. change::
- :tags: orm
- :tickets:
-
- a forward and complementing backwards reference which are both
- of the same direction, i.e. ONETOMANY or MANYTOONE,
- is now detected, and an error message is raised.
- Saves crazy CircularDependencyErrors later on.
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed bugs in Query regarding simultaneous selection of
- multiple joined-table inheritance entities with common base
- classes:
-
- - previously the adaption applied to "B" on
- "A JOIN B" would be erroneously partially applied
- to "A".
-
- - comparisons on relations (i.e. A.related==someb)
- were not getting adapted when they should.
-
- - Other filterings, like
- query(A).join(A.bs).filter(B.foo=='bar'), were erroneously
- adapting "B.foo" as though it were an "A".
-
- .. change::
- :tags: orm
- :tickets: 1325
-
- Fixed adaptation of EXISTS clauses via any(), has(), etc.
- in conjunction with an aliased object on the left and
- of_type() on the right.
-
- .. change::
- :tags: orm
- :tickets:
-
- Added an attribute helper method ``set_committed_value`` in
- sqlalchemy.orm.attributes. Given an object, attribute name,
- and value, will set the value on the object as part of its
- "committed" state, i.e. state that is understood to have
- been loaded from the database. Helps with the creation of
- homegrown collection loaders and such.
-
- .. change::
- :tags: orm
- :tickets:
-
- Query won't fail with weakref error when a non-mapper/class
- instrumented descriptor is passed, raises
- "Invalid column expression".
-
- .. change::
- :tags: orm
- :tickets:
-
- Query.group_by() properly takes into account aliasing applied
- to the FROM clause, such as with select_from(), using
- with_polymorphic(), or using from_self().
-
- .. change::
- :tags: sql
- :tickets:
-
- An alias() of a select() will convert to a "scalar subquery"
- when used in an unambiguously scalar context, i.e. it's used
- in a comparison operation. This applies to
- the ORM when using query.subquery() as well.
-
- .. change::
- :tags: sql
- :tickets: 1302
-
- Fixed missing _label attribute on Function object, others
- when used in a select() with use_labels (such as when used
- in an ORM column_property()).
-
- .. change::
- :tags: sql
- :tickets: 1309
-
- anonymous alias names now truncate down to the max length
- allowed by the dialect. More significant on DBs like
- Oracle with very small character limits.
-
- .. change::
- :tags: sql
- :tickets:
-
- the __selectable__() interface has been replaced entirely
- by __clause_element__().
-
- .. change::
- :tags: sql
- :tickets: 1299
-
- The per-dialect cache used by TypeEngine to cache
- dialect-specific types is now a WeakKeyDictionary.
- This to prevent dialect objects from
- being referenced forever for an application that
- creates an arbitrarily large number of engines
- or dialects. There is a small performance penalty
- which will be resolved in 0.6.
-
- .. change::
- :tags: sqlite
- :tickets:
-
- Fixed SQLite reflection methods so that non-present
- cursor.description, which triggers an auto-cursor
- close, will be detected so that no results doesn't
- fail on recent versions of pysqlite which raise
- an error when fetchone() called with no rows present.
-
- .. change::
- :tags: postgresql
- :tickets:
-
- Index reflection won't fail when an index with
- multiple expressions is encountered.
-
- .. change::
- :tags: postgresql
- :tickets: 1327
-
- Added PGUuid and PGBit types to
- sqlalchemy.databases.postgres.
-
- .. change::
- :tags: postgresql
- :tickets: 1327
-
- Refection of unknown PG types won't crash when those
- types are specified within a domain.
-
- .. change::
- :tags: mssql
- :tickets:
-
- Preliminary support for pymssql 1.0.1
-
- .. change::
- :tags: mssql
- :tickets:
-
- Corrected issue on mssql where max_identifier_length was
- not being respected.
-
- .. change::
- :tags: extensions
- :tickets:
-
- Fixed a recursive pickling issue in serializer, triggered
- by an EXISTS or other embedded FROM construct.
-
- .. change::
- :tags: extensions
- :tickets:
-
- Declarative locates the "inherits" class using a search
- through __bases__, to skip over mixins that are local
- to subclasses.
-
- .. change::
- :tags: extensions
- :tickets:
-
- Declarative figures out joined-table inheritance primary join
- condition even if "inherits" mapper argument is given
- explicitly.
-
- .. change::
- :tags: extensions
- :tickets:
-
- Declarative will properly interpret the "foreign_keys" argument
- on a backref() if it's a string.
-
- .. change::
- :tags: extensions
- :tickets:
-
- Declarative will accept a table-bound column as a property
- when used in conjunction with __table__, if the column is already
- present in __table__. The column will be remapped to the given
- key the same way as when added to the mapper() properties dict.
-
-.. changelog::
- :version: 0.5.2
- :released: Sat Jan 24 2009
-
- .. change::
- :tags: orm
- :tickets:
-
- Further refined 0.5.1's warning about delete-orphan cascade
- placed on a many-to-many relation. First, the bad news:
- the warning will apply to both many-to-many as well as
- many-to-one relations. This is necessary since in both
- cases, SQLA does not scan the full set of potential parents
- when determining "orphan" status - for a persistent object
- it only detects an in-python de-association event to establish
- the object as an "orphan". Next, the good news: to support
- one-to-one via a foreign key or association table, or to
- support one-to-many via an association table, a new flag
- single_parent=True may be set which indicates objects
- linked to the relation are only meant to have a single parent.
- The relation will raise an error if multiple parent-association
- events occur within Python.
-
- .. change::
- :tags: orm
- :tickets: 1292
-
- Adjusted the attribute instrumentation change from 0.5.1 to
- fully establish instrumentation for subclasses where the mapper
- was created after the superclass had already been fully
- instrumented.
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed bug in delete-orphan cascade whereby two one-to-one
- relations from two different parent classes to the same target
- class would prematurely expunge the instance.
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed an eager loading bug whereby self-referential eager
- loading would prevent other eager loads, self referential or not,
- from joining to the parent JOIN properly. Thanks to Alex K
- for creating a great test case.
-
- .. change::
- :tags: orm
- :tickets:
-
- session.expire() and related methods will not expire() unloaded
- deferred attributes. This prevents them from being needlessly
- loaded when the instance is refreshed.
-
- .. change::
- :tags: orm
- :tickets: 1293
-
- query.join()/outerjoin() will now properly join an aliased()
- construct to the existing left side, even if query.from_self()
- or query.select_from(someselectable) has been called.
-
- .. change::
- :tags: sql
- :tickets: 1284
-
- Further fixes to the "percent signs and spaces in column/table
- names" functionality.
-
- .. change::
- :tags: mssql
- :tickets: 1291
-
- Restored convert_unicode handling. Results were being passed
- on through without conversion.
-
- .. change::
- :tags: mssql
- :tickets: 1282
-
- Really fixing the decimal handling this time..
-
- .. change::
- :tags: Ticket:1289, mssql
- :tickets:
-
- Modified table reflection code to use only kwargs when
- constructing tables.
-
-.. changelog::
- :version: 0.5.1
- :released: Sat Jan 17 2009
-
- .. change::
- :tags: orm
- :tickets:
-
- Removed an internal join cache which could potentially leak
- memory when issuing query.join() repeatedly to ad-hoc
- selectables.
-
- .. change::
- :tags: orm
- :tickets:
-
- The "clear()", "save()", "update()", "save_or_update()"
- Session methods have been deprecated, replaced by
- "expunge_all()" and "add()". "expunge_all()" has also
- been added to ScopedSession.
-
- .. change::
- :tags: orm
- :tickets:
-
- Modernized the "no mapped table" exception and added a more
- explicit __table__/__tablename__ exception to declarative.
-
- .. change::
- :tags: orm
- :tickets: 1237
-
- Concrete inheriting mappers now instrument attributes which
- are inherited from the superclass, but are not defined for
- the concrete mapper itself, with an InstrumentedAttribute that
- issues a descriptive error when accessed.
-
- .. change::
- :tags: orm
- :tickets: 1237, 781
-
- Added a new `relation()` keyword `back_populates`. This
- allows configuration of backreferences using explicit
- relations. This is required when creating
- bidirectional relations between a hierarchy of concrete
- mappers and another class.
-
- .. change::
- :tags: orm
- :tickets: 1237
-
- Test coverage added for `relation()` objects specified on
- concrete mappers.
-
- .. change::
- :tags: orm
- :tickets: 1276
-
- Query.from_self() as well as query.subquery() both disable
- the rendering of eager joins inside the subquery produced.
- The "disable all eager joins" feature is available publicly
- via a new query.enable_eagerloads() generative.
-
- .. change::
- :tags: orm
- :tickets:
-
- Added a rudimental series of set operations to Query that
- receive Query objects as arguments, including union(),
- union_all(), intersect(), except_(), intersect_all(),
- except_all(). See the API documentation for
- Query.union() for examples.
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed bug that prevented Query.join() and eagerloads from
- attaching to a query that selected from a union or aliased union.
-
- .. change::
- :tags: orm
- :tickets: 1237
-
- A short documentation example added for bidirectional
- relations specified on concrete mappers.
-
- .. change::
- :tags: orm
- :tickets: 1269
-
- Mappers now instrument class attributes upon construction
- with the final InstrumentedAttribute object which remains
- persistent. The `_CompileOnAttr`/`__getattribute__()`
- methodology has been removed. The net effect is that
- Column-based mapped class attributes can now be used fully
- at the class level without invoking a mapper compilation
- operation, greatly simplifying typical usage patterns
- within declarative.
-
- .. change::
- :tags: orm
- :tickets:
-
- ColumnProperty (and front-end helpers such as ``deferred``) no
- longer ignores unknown \**keyword arguments.
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed a bug with the unitofwork's "row switch" mechanism,
- i.e. the conversion of INSERT/DELETE into an UPDATE, when
- combined with joined-table inheritance and an object
- which contained no defined values for the child table where
- an UPDATE with no SET clause would be rendered.
-
- .. change::
- :tags: orm
- :tickets: 1281
-
- Using delete-orphan on a many-to-many relation is deprecated.
- This produces misleading or erroneous results since SQLA does
- not retrieve the full list of "parents" for m2m. To get delete-orphan
- behavior with an m2m table, use an explicit association class
- so that the individual association row is treated as a parent.
-
- .. change::
- :tags: orm
- :tickets: 1281
-
- delete-orphan cascade always requires delete cascade. Specifying
- delete-orphan without delete now raises a deprecation warning.
-
- .. change::
- :tags: sql
- :tickets: 1256
-
- Improved the methodology to handling percent signs in column
- names from. Added more tests. MySQL and
- PostgreSQL dialects still do not issue correct CREATE TABLE
- statements for identifiers with percent signs in them.
-
- .. change::
- :tags: schema
- :tickets: 1214
-
- Index now accepts column-oriented InstrumentedAttributes
- (i.e. column-based mapped class attributes) as column
- arguments.
-
- .. change::
- :tags: schema
- :tickets:
-
- Column with no name (as in declarative) won't raise a
- NoneType error when its string output is requested
- (such as in a stack trace).
-
- .. change::
- :tags: schema
- :tickets: 1278
-
- Fixed bug when overriding a Column with a ForeignKey
- on a reflected table, where derived columns (i.e. the
- "virtual" columns of a select, etc.) would inadvertently
- call upon schema-level cleanup logic intended only
- for the original column.
-
- .. change::
- :tags: declarative
- :tickets:
-
- Can now specify Column objects on subclasses which have no
- table of their own (i.e. use single table inheritance).
- The columns will be appended to the base table, but only
- mapped by the subclass.
-
- .. change::
- :tags: declarative
- :tickets:
-
- For both joined and single inheriting subclasses, the subclass
- will only map those columns which are already mapped on the
- superclass and those explicit on the subclass. Other
- columns that are present on the `Table` will be excluded
- from the mapping by default, which can be disabled
- by passing a blank `exclude_properties` collection to the
- `__mapper_args__`. This is so that single-inheriting
- classes which define their own columns are the only classes
- to map those columns. The effect is actually a more organized
- mapping than you'd normally get with explicit `mapper()`
- calls unless you set up the `exclude_properties` arguments
- explicitly.
-
- .. change::
- :tags: declarative
- :tickets:
-
- It's an error to add new Column objects to a declarative class
- that specified an existing table using __table__.
-
- .. change::
- :tags: mysql
- :tickets:
-
- Added the missing keywords from MySQL 4.1 so they get escaped
- properly.
-
- .. change::
- :tags: mssql
- :tickets: 1280
-
- Corrected handling of large decimal values with more robust
- tests. Removed string manipulation on floats.
-
- .. change::
- :tags: mssql
- :tickets:
-
- Modified the do_begin handling in mssql to use the Cursor not
- the Connection so it is DBAPI compatible.
-
- .. change::
- :tags: mssql
- :tickets:
-
- Corrected SAVEPOINT support on adodbapi by changing the
- handling of savepoint_release, which is unsupported on mssql.
-
-.. changelog::
- :version: 0.5.0
- :released: Tue Jan 06 2009
-
- .. change::
- :tags: general
- :tickets:
-
- Documentation has been converted to Sphinx. In particular,
- the generated API documentation has been constructed into a
- full blown "API Reference" section which organizes editorial
- documentation combined with generated docstrings. Cross
- linking between sections and API docs are vastly improved, a
- javascript-powered search feature is provided, and a full
- index of all classes, functions and members is provided.
-
- .. change::
- :tags: general
- :tickets:
-
- setup.py now imports setuptools only optionally. If not
- present, distutils is used. The new "pip" installer is
- recommended over easy_install as it installs in a more
- simplified way.
-
- .. change::
- :tags: general
- :tickets:
-
- added an extremely basic illustration of a PostGIS integration
- to the examples folder.
-
- .. change::
- :tags: orm
- :tickets:
-
- Query.with_polymorphic() now accepts a third argument
- "discriminator" which will replace the value of
- mapper.polymorphic_on for that query. Mappers themselves no
- longer require polymorphic_on to be set, even if the mapper
- has a polymorphic_identity. When not set, the mapper will
- load non-polymorphically by default. Together, these two
- features allow a non-polymorphic concrete inheritance setup to
- use polymorphic loading on a per-query basis, since concrete
- setups are prone to many issues when used polymorphically in
- all cases.
-
- .. change::
- :tags: orm
- :tickets:
-
- dynamic_loader accepts a query_class= to customize the Query
- classes used for both the dynamic collection and the queries
- built from it.
-
- .. change::
- :tags: orm
- :tickets: 1079
-
- query.order_by() accepts None which will remove any pending
- order_by state from the query, as well as cancel out any
- mapper/relation configured ordering. This is primarily useful
- for overriding the ordering specified on a dynamic_loader().
-
- .. change::
- :tags: sql
- :tickets: 935
-
- RowProxy objects can be used in place of dictionary arguments
- sent to connection.execute() and friends.
-
- .. change::
- :tags: dialect
- :tickets:
-
- Added a new description_encoding attribute on the dialect that
- is used for encoding the column name when processing the
- metadata. This usually defaults to utf-8.
-
- .. change::
- :tags: mssql
- :tickets:
-
- Added in a new MSGenericBinary type. This maps to the Binary
- type so it can implement the specialized behavior of treating
- length specified types as fixed-width Binary types and
- non-length types as an unbound variable length Binary type.
-
- .. change::
- :tags: mssql
- :tickets: 1249
-
- Added in new types: MSVarBinary and MSImage.
-
- .. change::
- :tags: mssql
- :tickets:
-
- Added in the MSReal, MSNText, MSSmallDateTime, MSTime,
- MSDateTimeOffset, and MSDateTime2 types
-
- .. change::
- :tags: sqlite
- :tickets: 1266
-
- Table reflection now stores the actual DefaultClause value for
- the column.
-
- .. change::
- :tags: sqlite
- :tickets:
-
- bugfixes, behavioral changes
-
- .. change::
- :tags: orm
- :tickets:
-
- Exceptions raised during compile_mappers() are now preserved
- to provide "sticky behavior" - if a hasattr() call on a
- pre-compiled mapped attribute triggers a failing compile and
- suppresses the exception, subsequent compilation is blocked
- and the exception will be reiterated on the next compile()
- call. This issue occurs frequently when using declarative.
-
- .. change::
- :tags: orm
- :tickets:
-
- property.of_type() is now recognized on a single-table
- inheriting target, when used in the context of
- prop.of_type(..).any()/has(), as well as
- query.join(prop.of_type(...)).
-
- .. change::
- :tags: orm
- :tickets:
-
- query.join() raises an error when the target of the join
- doesn't match the property-based attribute - while it's
- unlikely anyone is doing this, the SQLAlchemy author was
- guilty of this particular loosey-goosey behavior.
-
- .. change::
- :tags: orm
- :tickets: 1272
-
- Fixed bug when using weak_instance_map=False where modified
- events would not be intercepted for a flush().
-
- .. change::
- :tags: orm
- :tickets: 1268
-
- Fixed some deep "column correspondence" issues which could
- impact a Query made against a selectable containing multiple
- versions of the same table, as well as unions and similar
- which contained the same table columns in different column
- positions at different levels.
-
- .. change::
- :tags: orm
- :tickets:
-
- Custom comparator classes used in conjunction with
- column_property(), relation() etc. can define new comparison
- methods on the Comparator, which will become available via
- __getattr__() on the InstrumentedAttribute. In the case of
- synonym() or comparable_property(), attributes are resolved
- first on the user-defined descriptor, then on the user-defined
- comparator.
-
- .. change::
- :tags: orm
- :tickets: 976
-
- Added ScopedSession.is_active accessor.
-
- .. change::
- :tags: orm
- :tickets: 1262
-
- Can pass mapped attributes and column objects as keys to
- query.update({}).
-
- .. change::
- :tags: orm
- :tickets:
-
- Mapped attributes passed to the values() of an expression
- level insert() or update() will use the keys of the mapped
- columns, not that of the mapped attribute.
-
- .. change::
- :tags: orm
- :tickets: 1242
-
- Corrected problem with Query.delete() and Query.update() not
- working properly with bind parameters.
-
- .. change::
- :tags: orm
- :tickets:
-
- Query.select_from(), from_statement() ensure that the given
- argument is a FromClause, or Text/Select/Union, respectively.
-
- .. change::
- :tags: orm
- :tickets: 1253
-
- Query() can be passed a "composite" attribute as a column
- expression and it will be expanded. Somewhat related to.
-
- .. change::
- :tags: orm
- :tickets:
-
- Query() is a little more robust when passed various column
- expressions such as strings, clauselists, text() constructs
- (which may mean it just raises an error more nicely).
-
- .. change::
- :tags: orm
- :tickets:
-
- first() works as expected with Query.from_statement().
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed bug introduced in 0.5rc4 involving eager loading not
- functioning for properties which were added to a mapper
- post-compile using add_property() or equivalent.
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed bug where many-to-many relation() with viewonly=True
- would not correctly reference the link between
- secondary->remote.
-
- .. change::
- :tags: orm
- :tickets: 1232
-
- Duplicate items in a list-based collection will be maintained
- when issuing INSERTs to a "secondary" table in a many-to-many
- relation. Assuming the m2m table has a unique or primary key
- constraint on it, this will raise the expected constraint
- violation instead of silently dropping the duplicate
- entries. Note that the old behavior remains for a one-to-many
- relation since collection entries in that case don't result in
- INSERT statements and SQLA doesn't manually police
- collections.
-
- .. change::
- :tags: orm
- :tickets:
-
- Query.add_column() can accept FromClause objects in the same
- manner as session.query() can.
-
- .. change::
- :tags: orm
- :tickets:
-
- Comparison of many-to-one relation to NULL is properly
- converted to IS NOT NULL based on not_().
-
- .. change::
- :tags: orm
- :tickets: 1087
-
- Extra checks added to ensure explicit
- primaryjoin/secondaryjoin are ClauseElement instances, to
- prevent more confusing errors later on.
-
- .. change::
- :tags: orm
- :tickets: 1236
-
- Improved mapper() check for non-class classes.
-
- .. change::
- :tags: orm
- :tickets: 5051
-
- comparator_factory argument is now documented and supported by
- all MapperProperty types, including column_property(),
- relation(), backref(), and synonym().
-
- .. change::
- :tags: orm
- :tickets:
-
- Changed the name of PropertyLoader to RelationProperty, to be
- consistent with all the other names. PropertyLoader is still
- present as a synonym.
-
- .. change::
- :tags: orm
- :tickets: 1099, 1228
-
- fixed "double iter()" call causing bus errors in shard API,
- removed errant result.close() left over from the 0.4
- version.
-
- .. change::
- :tags: orm
- :tickets:
-
- made Session.merge cascades not trigger autoflush. Fixes
- merged instances getting prematurely inserted with missing
- values.
-
- .. change::
- :tags: orm
- :tickets:
-
- Two fixes to help prevent out-of-band columns from being
- rendered in polymorphic_union inheritance scenarios (which
- then causes extra tables to be rendered in the FROM clause
- causing cartesian products):
-
- - improvements to "column adaption" for a->b->c inheritance
- situations to better locate columns that are related to
- one another via multiple levels of indirection, rather
- than rendering the non-adapted column.
-
- - the "polymorphic discriminator" column is only rendered
- for the actual mapper being queried against. The column
- won't be "pulled in" from a subclass or superclass mapper
- since it's not needed.
-
- .. change::
- :tags: orm
- :tickets: 1072
-
- Fixed shard_id argument on ShardedSession.execute().
-
- .. change::
- :tags: sql
- :tickets: 1256
-
- Columns can again contain percent signs within their
- names.
-
- .. change::
- :tags: sql
- :tickets:
-
- sqlalchemy.sql.expression.Function is now a public class. It
- can be subclassed to provide user-defined SQL functions in an
- imperative style, including with pre-established behaviors.
- The postgis.py example illustrates one usage of this.
-
- .. change::
- :tags: sql
- :tickets:
-
- PickleType now favors == comparison by default, if the
- incoming object (such as a dict) implements __eq__(). If the
- object does not implement __eq__() and mutable=True, a
- deprecation warning is raised.
-
- .. change::
- :tags: sql
- :tickets: 1215
-
- Fixed the import weirdness in sqlalchemy.sql to not export
- __names__.
-
- .. change::
- :tags: sql
- :tickets: 1238
-
- Using the same ForeignKey object repeatedly raises an error
- instead of silently failing later.
-
- .. change::
- :tags: sql
- :tickets:
-
- Added NotImplementedError for params() method on
- Insert/Update/Delete constructs. These items currently don't
- support this functionality, which also would be a little
- misleading compared to values().
-
- .. change::
- :tags: sql
- :tickets: 650
-
- Reflected foreign keys will properly locate their referenced
- column, even if the column was given a "key" attribute
- different from the reflected name. This is achieved via a new
- flag on ForeignKey/ForeignKeyConstraint called "link_to_name",
- if True means the given name is the referred-to column's name,
- not its assigned key.
-
- .. change::
- :tags: sql
- :tickets: 1253
-
- select() can accept a ClauseList as a column in the same way
- as a Table or other selectable and the interior expressions
- will be used as column elements.
-
- .. change::
- :tags: sql
- :tickets:
-
- the "passive" flag on session.is_modified() is correctly
- propagated to the attribute manager.
-
- .. change::
- :tags: sql
- :tickets:
-
- union() and union_all() will not whack any order_by() that has
- been applied to the select()s inside. If you union() a
- select() with order_by() (presumably to support LIMIT/OFFSET),
- you should also call self_group() on it to apply parenthesis.
-
- .. change::
- :tags: engine/pool
- :tickets: 1246
-
- Connection.invalidate() checks for closed status to avoid
- attribute errors.
-
- .. change::
- :tags: engine/pool
- :tickets: 1094
-
- NullPool supports reconnect on failure behavior.
-
- .. change::
- :tags: engine/pool
- :tickets: 799
-
- Added a mutex for the initial pool creation when using
- pool.manage(dbapi). This prevents a minor case of "dogpile"
- behavior which would otherwise occur upon a heavy load
- startup.
-
- .. change::
- :tags: engine/pool
- :tickets:
-
- _execute_clauseelement() goes back to being a private method.
- Subclassing Connection is not needed now that ConnectionProxy
- is available.
-
- .. change::
- :tags: documentation
- :tickets: 1149, 1200
-
- Tickets.
-
- .. change::
- :tags: documentation
- :tickets:
-
- Added note about create_session() defaults.
-
- .. change::
- :tags: documentation
- :tickets:
-
- Added section about metadata.reflect().
-
- .. change::
- :tags: documentation
- :tickets:
-
- Updated `TypeDecorator` section.
-
- .. change::
- :tags: documentation
- :tickets:
-
- Rewrote the "threadlocal" strategy section of the docs due to
- recent confusion over this feature.
-
- .. change::
- :tags: documentation
- :tickets:
-
- Removed badly out of date 'polymorphic_fetch' and
- 'select_table' docs from inheritance, reworked the second half
- of "joined table inheritance".
-
- .. change::
- :tags: documentation
- :tickets:
-
- Documented `comparator_factory` kwarg, added new doc section
- "Custom Comparators".
-
- .. change::
- :tags: mssql
- :tickets: 1254
-
- Refactored the Date/Time types. The ``smalldatetime`` data
- type no longer truncates to a date only, and will now be
- mapped to the MSSmallDateTime type.
-
- .. change::
- :tags: mssql
- :tickets:
-
- Corrected an issue with Numerics to accept an int.
-
- .. change::
- :tags: mssql
- :tickets:
-
- Mapped ``char_length`` to the ``LEN()`` function.
-
- .. change::
- :tags: mssql
- :tickets:
-
- If an ``INSERT`` includes a subselect the ``INSERT`` is
- converted from an ``INSERT INTO VALUES`` construct to a
- ``INSERT INTO SELECT`` construct.
-
- .. change::
- :tags: mssql
- :tickets:
-
- If the column is part of a ``primary_key`` it will be ``NOT
- NULL`` since MSSQL doesn't allow ``NULL`` in primary_key
- columns.
-
- .. change::
- :tags: mssql
- :tickets: 1249
-
- ``MSBinary`` now returns a ``BINARY`` instead of an
- ``IMAGE``. This is a backwards incompatible change in that
- ``BINARY`` is a fixed length data type whereas ``IMAGE`` is a
- variable length data type.
-
- .. change::
- :tags: mssql
- :tickets: 1258
-
- ``get_default_schema_name`` is now reflected from the database
- based on the user's default schema. This only works with MSSQL
- 2005 and later.
-
- .. change::
- :tags: mssql
- :tickets: 1248
-
- Added collation support through the use of a new collation
- argument. This is supported on the following types: char,
- nchar, varchar, nvarchar, text, ntext.
-
- .. change::
- :tags: mssql
- :tickets:
-
- Changes to the connection string parameters favor DSN as the
- default specification for pyodbc. See the mssql.py docstring
- for detailed usage instructions.
-
- .. change::
- :tags: mssql
- :tickets:
-
- Added experimental support of savepoints. It currently does
- not work fully with sessions.
-
- .. change::
- :tags: mssql
- :tickets: 1243
-
- Support for three levels of column nullability: NULL, NOT
- NULL, and the database's configured default. The default
- Column configuration (nullable=True) will now generate NULL in
- the DDL. Previously no specification was emitted and the
- database default would take effect (usually NULL, but not
- always). To explicitly request the database default,
- configure columns with nullable=None and no specification will
- be emitted in DDL. This is backwards incompatible
- behavior.
-
- .. change::
- :tags: postgres
- :tickets: 1267
-
- "%" signs in text() constructs are automatically escaped to
- "%%". Because of the backwards incompatible nature of this
- change, a warning is emitted if '%%' is detected in the
- string.
-
- .. change::
- :tags: postgres
- :tickets:
-
- Calling alias.execute() in conjunction with
- server_side_cursors won't raise AttributeError.
-
- .. change::
- :tags: postgres
- :tickets: 714
-
- Added Index reflection support to PostgreSQL, using a great
- patch we long neglected, submitted by Ken
- Kuhlman.
-
- .. change::
- :tags: oracle
- :tickets:
-
- Adjusted the format of create_xid() to repair two-phase
- commit. We now have field reports of Oracle two-phase commit
- working properly with this change.
-
- .. change::
- :tags: oracle
- :tickets: 1233
-
- Added OracleNVarchar type, produces NVARCHAR2, and also
- subclasses Unicode so that convert_unicode=True by default.
- NVARCHAR2 reflects into this type automatically so these
- columns pass unicode on a reflected table with no explicit
- convert_unicode=True flags.
-
- .. change::
- :tags: oracle
- :tickets: 1265
-
- Fixed bug which was preventing out params of certain types
- from being received; thanks a ton to huddlej at wwu.edu !
-
- .. change::
- :tags: mysql
- :tickets:
-
- "%" signs in text() constructs are automatically escaped to
- "%%". Because of the backwards incompatible nature of this
- change, a warning is emitted if '%%' is detected in the
- string.
-
- .. change::
- :tags: mysql
- :tickets: 1241
-
- Fixed bug in exception raise when FK columns not present
- during reflection.
-
- .. change::
- :tags: mysql
- :tickets:
-
- Fixed bug involving reflection of a remote-schema table with a
- foreign key ref to another table in that schema.
-
- .. change::
- :tags: associationproxy
- :tickets:
-
- The association proxy properties are make themselves available
- at the class level, e.g. MyClass.aproxy. Previously this
- evaluated to None.
-
- .. change::
- :tags: declarative
- :tickets:
-
- The full list of arguments accepted as string by backref()
- includes 'primaryjoin', 'secondaryjoin', 'secondary',
- 'foreign_keys', 'remote_side', 'order_by'.
-
-.. changelog::
- :version: 0.5.0rc4
- :released: Fri Nov 14 2008
-
- .. change::
- :tags: orm
- :tickets:
-
- Query.count() has been enhanced to do the "right thing" in a
- wider variety of cases. It can now count multiple-entity
- queries, as well as column-based queries. Note that this means
- if you say query(A, B).count() without any joining criterion,
- it's going to count the cartesian product of A*B. Any query
- which is against column-based entities will automatically
- issue "SELECT count(1) FROM (SELECT...)" so that the real
- rowcount is returned, meaning a query such as
- query(func.count(A.name)).count() will return a value of one,
- since that query would return one row.
-
- .. change::
- :tags: orm
- :tickets:
-
- Lots of performance tuning. A rough guesstimate over various
- ORM operations places it 10% faster over 0.5.0rc3, 25-30% over
- 0.4.8.
-
- .. change::
- :tags: orm
- :tickets:
-
- bugfixes and behavioral changes
-
- .. change::
- :tags: general
- :tickets:
-
- global "propigate"->"propagate" change.
-
- .. change::
- :tags: orm
- :tickets:
-
- Adjustments to the enhanced garbage collection on
- InstanceState to better guard against errors due to lost
- state.
-
- .. change::
- :tags: orm
- :tickets: 1220
-
- Query.get() returns a more informative error message when
- executed against multiple entities.
-
- .. change::
- :tags: orm
- :tickets: 1140, 1221
-
- Restored NotImplementedError on Cls.relation.in_()
-
- .. change::
- :tags: orm
- :tickets: 1226
-
- Fixed PendingDeprecationWarning involving order_by parameter
- on relation().
-
- .. change::
- :tags: sql
- :tickets:
-
- Removed the 'properties' attribute of the Connection object,
- Connection.info should be used.
-
- .. change::
- :tags: sql
- :tickets:
-
- Restored "active rowcount" fetch before ResultProxy autocloses
- the cursor. This was removed in 0.5rc3.
-
- .. change::
- :tags: sql
- :tickets:
-
- Rearranged the `load_dialect_impl()` method in `TypeDecorator`
- such that it will take effect even if the user-defined
- `TypeDecorator` uses another `TypeDecorator` as its impl.
-
- .. change::
- :tags: access
- :tickets:
-
- Added support for Currency type.
-
- .. change::
- :tags: access
- :tickets: 1017
-
- Functions were not return their result.
-
- .. change::
- :tags: access
- :tickets: 1017
-
- Corrected problem with joins. Access only support LEFT OUTER
- or INNER not just JOIN by itself.
-
- .. change::
- :tags: mssql
- :tickets:
-
- Lots of cleanup and fixes to correct problems with limit and
- offset.
-
- .. change::
- :tags: mssql
- :tickets:
-
- Correct situation where subqueries as part of a binary
- expression need to be translated to use the IN and NOT IN
- syntax.
-
- .. change::
- :tags: mssql
- :tickets: 1216
-
- Fixed E Notation issue that prevented the ability to insert
- decimal values less than 1E-6.
-
- .. change::
- :tags: mssql
- :tickets: 1217
-
- Corrected problems with reflection when dealing with schemas,
- particularly when those schemas are the default
- schema.
-
- .. change::
- :tags: mssql
- :tickets:
-
- Corrected problem with casting a zero length item to a
- varchar. It now correctly adjusts the CAST.
-
- .. change::
- :tags: ext
- :tickets:
-
- Can now use a custom "inherit_condition" in __mapper_args__
- when using declarative.
-
- .. change::
- :tags: ext
- :tickets:
-
- fixed string-based "remote_side", "order_by" and others not
- propagating correctly when used in backref().
-
-.. changelog::
- :version: 0.5.0rc3
- :released: Fri Nov 07 2008
-
- .. change::
- :tags: orm
- :tickets:
-
- Added two new hooks to SessionExtension: after_bulk_delete()
- and after_bulk_update(). after_bulk_delete() is called after
- a bulk delete() operation on a query. after_bulk_update() is
- called after a bulk update() operation on a query.
-
- .. change::
- :tags: sql
- :tickets:
-
- SQL compiler optimizations and complexity reduction. The call
- count for compiling a typical select() construct is 20% less
- versus 0.5.0rc2.
-
- .. change::
- :tags: sql
- :tickets: 1211
-
- Dialects can now generate label names of adjustable
- length. Pass in the argument "label_length=<value>" to
- create_engine() to adjust how many characters max will be
- present in dynamically generated column labels, i.e.
- "somecolumn AS somelabel". Any value less than 6 will result
- in a label of minimal size, consisting of an underscore and a
- numeric counter. The compiler uses the value of
- dialect.max_identifier_length as a default.
-
- .. change::
- :tags: ext
- :tickets:
-
- Added a new extension sqlalchemy.ext.serializer. Provides
- Serializer/Deserializer "classes" which mirror
- Pickle/Unpickle, as well as dumps() and loads(). This
- serializer implements an "external object" pickler which keeps
- key context-sensitive objects, including engines, sessions,
- metadata, Tables/Columns, and mappers, outside of the pickle
- stream, and can later restore the pickle using any
- engine/metadata/session provider. This is used not for
- pickling regular object instances, which are pickleable
- without any special logic, but for pickling expression objects
- and full Query objects, such that all mapper/engine/session
- dependencies can be restored at unpickle time.
-
- .. change::
- :tags: oracle
- :tickets:
-
- Wrote a docstring for Oracle dialect. Apparently that Ohloh
- "few source code comments" label is starting to string :).
-
- .. change::
- :tags: oracle
- :tickets: 536
-
- Removed FIRST_ROWS() optimize flag when using LIMIT/OFFSET,
- can be re-enabled with optimize_limits=True create_engine()
- flag.
-
- .. change::
- :tags: oracle
- :tickets:
-
- bugfixes and behavioral changes
-
- .. change::
- :tags: orm
- :tickets:
-
- "not equals" comparisons of simple many-to-one relation to an
- instance will not drop into an EXISTS clause and will compare
- foreign key columns instead.
-
- .. change::
- :tags: orm
- :tickets:
-
- Removed not-really-working use cases of comparing a collection
- to an iterable. Use contains() to test for collection
- membership.
-
- .. change::
- :tags: orm
- :tickets: 1171
-
- Improved the behavior of aliased() objects such that they more
- accurately adapt the expressions generated, which helps
- particularly with self-referential comparisons.
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed bug involving primaryjoin/secondaryjoin conditions
- constructed from class-bound attributes (as often occurs when
- using declarative), which later would be inappropriately
- aliased by Query, particularly with the various EXISTS based
- comparators.
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed bug when using multiple query.join() with an
- aliased-bound descriptor which would lose the left alias.
-
- .. change::
- :tags: orm
- :tickets:
-
- Improved weakref identity map memory management to no longer
- require mutexing, resurrects garbage collected instance on a
- lazy basis for an InstanceState with pending changes.
-
- .. change::
- :tags: orm
- :tickets:
-
- InstanceState object now removes circular references to itself
- upon disposal to keep it outside of cyclic garbage collection.
-
- .. change::
- :tags: orm
- :tickets:
-
- relation() won't hide unrelated ForeignKey errors inside of
- the "please specify primaryjoin" message when determining join
- condition.
-
- .. change::
- :tags: orm
- :tickets: 1218
-
- Fixed bug in Query involving order_by() in conjunction with
- multiple aliases of the same class (will add tests in)
-
- .. change::
- :tags: orm
- :tickets:
-
- When using Query.join() with an explicit clause for the ON
- clause, the clause will be aliased in terms of the left side
- of the join, allowing scenarios like query(Source).
- from_self().join((Dest, Source.id==Dest.source_id)) to work
- properly.
-
- .. change::
- :tags: orm
- :tickets:
-
- polymorphic_union() function respects the "key" of each Column
- if they differ from the column's name.
-
- .. change::
- :tags: orm
- :tickets: 1183
-
- Repaired support for "passive-deletes" on a many-to-one
- relation() with "delete" cascade.
-
- .. change::
- :tags: orm
- :tickets: 1213
-
- Fixed bug in composite types which prevented a primary-key
- composite type from being mutated.
-
- .. change::
- :tags: orm
- :tickets: 1202
-
- Added more granularity to internal attribute access, such that
- cascade and flush operations will not initialize unloaded
- attributes and collections, leaving them intact for a
- lazy-load later on. Backref events still initialize attributes
- and collections for pending instances.
-
- .. change::
- :tags: sql
- :tickets: 1212
-
- Simplified the check for ResultProxy "autoclose without
- results" to be based solely on presence of
- cursor.description. All the regexp-based guessing about
- statements returning rows has been removed.
-
- .. change::
- :tags: sql
- :tickets: 1194
-
- Direct execution of a union() construct will properly set up
- result-row processing.
-
- .. change::
- :tags: sql
- :tickets:
-
- The internal notion of an "OID" or "ROWID" column has been
- removed. It's basically not used by any dialect, and the
- possibility of its usage with psycopg2's cursor.lastrowid is
- basically gone now that INSERT..RETURNING is available.
-
- .. change::
- :tags: sql
- :tickets:
-
- Removed "default_order_by()" method on all FromClause objects.
-
- .. change::
- :tags: sql
- :tickets:
-
- Repaired the table.tometadata() method so that a passed-in
- schema argument is propagated to ForeignKey constructs.
-
- .. change::
- :tags: sql
- :tickets:
-
- Slightly changed behavior of IN operator for comparing to
- empty collections. Now results in inequality comparison
- against self. More portable, but breaks with stored procedures
- that aren't pure functions.
-
- .. change::
- :tags: oracle
- :tickets:
-
- Setting the auto_convert_lobs to False on create_engine() will
- also instruct the OracleBinary type to return the cx_oracle
- LOB object unchanged.
-
- .. change::
- :tags: mysql
- :tickets:
-
- Fixed foreign key reflection in the edge case where a Table's
- explicit schema= is the same as the schema (database) the
- connection is attached to.
-
- .. change::
- :tags: mysql
- :tickets:
-
- No longer expects include_columns in table reflection to be
- lower case.
-
- .. change::
- :tags: ext
- :tickets: 1174
-
- Fixed bug preventing declarative-bound "column" objects from
- being used in column_mapped_collection().
-
- .. change::
- :tags: misc
- :tickets: 1077
-
- util.flatten_iterator() func doesn't interpret strings with
- __iter__() methods as iterators, such as in pypy.
-
-.. changelog::
- :version: 0.5.0rc2
- :released: Sun Oct 12 2008
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed bug involving read/write relation()s that contain
- literal or other non-column expressions within their
- primaryjoin condition equated to a foreign key column.
-
- .. change::
- :tags: orm
- :tickets:
-
- "non-batch" mode in mapper(), a feature which allows mapper
- extension methods to be called as each instance is
- updated/inserted, now honors the insert order of the objects
- given.
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed RLock-related bug in mapper which could deadlock upon
- reentrant mapper compile() calls, something that occurs when
- using declarative constructs inside of ForeignKey objects.
-
- .. change::
- :tags: orm
- :tickets:
-
- ScopedSession.query_property now accepts a query_cls factory,
- overriding the session's configured query_cls.
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed shared state bug interfering with ScopedSession.mapper's
- ability to apply default __init__ implementations on object
- subclasses.
-
- .. change::
- :tags: orm
- :tickets: 1177
-
- Fixed up slices on Query (i.e. query[x:y]) to work properly
- for zero length slices, slices with None on either end.
-
- .. change::
- :tags: orm
- :tickets:
-
- Added an example illustrating Celko's "nested sets" as a
- SQLA mapping.
-
- .. change::
- :tags: orm
- :tickets:
-
- contains_eager() with an alias argument works even when
- the alias is embedded in a SELECT, as when sent to the
- Query via query.select_from().
-
- .. change::
- :tags: orm
- :tickets: 1180
-
- contains_eager() usage is now compatible with a Query that
- also contains a regular eager load and limit/offset, in that
- the columns are added to the Query-generated subquery.
-
- .. change::
- :tags: orm
- :tickets:
-
- session.execute() will execute a Sequence object passed to
- it (regression from 0.4).
-
- .. change::
- :tags: orm
- :tickets:
-
- Removed the "raiseerror" keyword argument from object_mapper()
- and class_mapper(). These functions raise in all cases
- if the given class/instance is not mapped.
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed session.transaction.commit() on a autocommit=False
- session not starting a new transaction.
-
- .. change::
- :tags: orm
- :tickets:
-
- Some adjustments to Session.identity_map's weak referencing
- behavior to reduce asynchronous GC side effects.
-
- .. change::
- :tags: orm
- :tickets: 1182
-
- Adjustment to Session's post-flush accounting of newly
- "clean" objects to better protect against operating on
- objects as they're asynchronously gc'ed.
-
- .. change::
- :tags: sql
- :tickets: 1074
-
- column.in_(someselect) can now be used as a columns-clause
- expression without the subquery bleeding into the FROM clause
-
- .. change::
- :tags: sqlite
- :tickets: 968
-
- Overhauled SQLite date/time bind/result processing to use
- regular expressions and format strings, rather than
- strptime/strftime, to generically support pre-1900 dates,
- dates with microseconds.
-
- .. change::
- :tags: sqlite
- :tickets:
-
- String's (and Unicode's, UnicodeText's, etc.) convert_unicode
- logic disabled in the sqlite dialect, to adjust for pysqlite
- 2.5.0's new requirement that only Python unicode objects are
- accepted;
- https://itsystementwicklung.de/pipermail/list-pysqlite/2008-March/000018.html
-
- .. change::
- :tags: mysql
- :tickets:
-
- Temporary tables are now reflectable.
-
- .. change::
- :tags: oracle
- :tickets: 1187
-
- Oracle will detect string-based statements which contain
- comments at the front before a SELECT as SELECT statements.
-
-.. changelog::
- :version: 0.5.0rc1
- :released: Thu Sep 11 2008
-
- .. change::
- :tags: orm
- :tickets:
-
- Query now has delete() and update(values) methods. This allows
- to perform bulk deletes/updates with the Query object.
-
- .. change::
- :tags: orm
- :tickets:
-
- The RowTuple object returned by Query(\*cols) now features
- keynames which prefer mapped attribute names over column keys,
- column keys over column names, i.e. Query(Class.foo,
- Class.bar) will have names "foo" and "bar" even if those are
- not the names of the underlying Column objects. Direct Column
- objects such as Query(table.c.col) will return the "key"
- attribute of the Column.
-
- .. change::
- :tags: orm
- :tickets:
-
- Added scalar() and value() methods to Query, each return a
- single scalar value. scalar() takes no arguments and is
- roughly equivalent to first()[0], value()
- takes a single column expression and is roughly equivalent to
- values(expr).next()[0].
-
- .. change::
- :tags: orm
- :tickets:
-
- Improved the determination of the FROM clause when placing SQL
- expressions in the query() list of entities. In particular
- scalar subqueries should not "leak" their inner FROM objects
- out into the enclosing query.
-
- .. change::
- :tags: orm
- :tickets:
-
- Joins along a relation() from a mapped class to a mapped
- subclass, where the mapped subclass is configured with single
- table inheritance, will include an IN clause which limits the
- subtypes of the joined class to those requested, within the ON
- clause of the join. This takes effect for eager load joins as
- well as query.join(). Note that in some scenarios the IN
- clause will appear in the WHERE clause of the query as well
- since this discrimination has multiple trigger points.
-
- .. change::
- :tags: orm
- :tickets:
-
- AttributeExtension has been refined such that the event
- is fired before the mutation actually occurs. Additionally,
- the append() and set() methods must now return the given value,
- which is used as the value to be used in the mutation operation.
- This allows creation of validating AttributeListeners which
- raise before the action actually occurs, and which can change
- the given value into something else before its used.
-
- .. change::
- :tags: orm
- :tickets:
-
- column_property(), composite_property(), and relation() now
- accept a single or list of AttributeExtensions using the
- "extension" keyword argument.
-
- .. change::
- :tags: orm
- :tickets:
-
- query.order_by().get() silently drops the "ORDER BY" from
- the query issued by GET but does not raise an exception.
-
- .. change::
- :tags: orm
- :tickets:
-
- Added a Validator AttributeExtension, as well as a
- @validates decorator which is used in a similar fashion
- as @reconstructor, and marks a method as validating
- one or more mapped attributes.
-
- .. change::
- :tags: orm
- :tickets: 1140
-
- class.someprop.in_() raises NotImplementedError pending the
- implementation of "in\_" for relation
-
- .. change::
- :tags: orm
- :tickets: 1127
-
- Fixed primary key update for many-to-many collections where
- the collection had not been loaded yet
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed bug whereby deferred() columns with a group in conjunction
- with an otherwise unrelated synonym() would produce
- an AttributeError during deferred load.
-
- .. change::
- :tags: orm
- :tickets: 1128
-
- The before_flush() hook on SessionExtension takes place before
- the list of new/dirty/deleted is calculated for the final
- time, allowing routines within before_flush() to further
- change the state of the Session before the flush proceeds.
-
- .. change::
- :tags: orm
- :tickets:
-
- The "extension" argument to Session and others can now
- optionally be a list, supporting events sent to multiple
- SessionExtension instances. Session places SessionExtensions
- in Session.extensions.
-
- .. change::
- :tags: orm
- :tickets:
-
- Reentrant calls to flush() raise an error. This also serves
- as a rudimentary, but not foolproof, check against concurrent
- calls to Session.flush().
-
- .. change::
- :tags: orm
- :tickets:
-
- Improved the behavior of query.join() when joining to
- joined-table inheritance subclasses, using explicit join
- criteria (i.e. not on a relation).
-
- .. change::
- :tags: orm
- :tickets:
-
- @orm.attributes.reconstitute and
- MapperExtension.reconstitute have been renamed to
- @orm.reconstructor and MapperExtension.reconstruct_instance
-
- .. change::
- :tags: orm
- :tickets: 1129
-
- Fixed @reconstructor hook for subclasses which inherit from a
- base class.
-
- .. change::
- :tags: orm
- :tickets: 1132
-
- The composite() property type now supports a
- __set_composite_values__() method on the composite class which
- is required if the class represents state using attribute
- names other than the column's keynames; default-generated
- values now get populated properly upon flush. Also,
- composites with attributes set to None compare correctly.
-
- .. change::
- :tags: orm
- :tickets:
-
- The 3-tuple of iterables returned by attributes.get_history()
- may now be a mix of lists and tuples. (Previously members
- were always lists.)
-
- .. change::
- :tags: orm
- :tickets: 1151
-
- Fixed bug whereby changing a primary key attribute on an
- entity where the attribute's previous value had been expired
- would produce an error upon flush().
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed custom instrumentation bug whereby get_instance_dict()
- was not called for newly constructed instances not loaded
- by the ORM.
-
- .. change::
- :tags: orm
- :tickets: 1150
-
- Session.delete() adds the given object to the session if
- not already present. This was a regression bug from 0.4.
-
- .. change::
- :tags: orm
- :tickets:
-
- The `echo_uow` flag on `Session` is deprecated, and unit-of-work
- logging is now application-level only, not per-session level.
-
- .. change::
- :tags: orm
- :tickets: 1153
-
- Removed conflicting `contains()` operator from
- `InstrumentedAttribute` which didn't accept `escape` kwaarg.
-
- .. change::
- :tags: declarative
- :tickets: 1161
-
- Fixed bug whereby mapper couldn't initialize if a composite
- primary key referenced another table that was not defined
- yet.
-
- .. change::
- :tags: declarative
- :tickets:
-
- Fixed exception throw which would occur when string-based
- primaryjoin condition was used in conjunction with backref.
-
- .. change::
- :tags: schema
- :tickets: 1033
-
- Added "sorted_tables" accessor to MetaData, which returns
- Table objects sorted in order of dependency as a list.
- This deprecates the MetaData.table_iterator() method.
- The "reverse=False" keyword argument has also been
- removed from util.sort_tables(); use the Python
- 'reversed' function to reverse the results.
-
- .. change::
- :tags: schema
- :tickets:
-
- The 'length' argument to all Numeric types has been renamed
- to 'scale'. 'length' is deprecated and is still accepted
- with a warning.
-
- .. change::
- :tags: schema
- :tickets:
-
- Dropped 0.3-compatibility for user defined types
- (convert_result_value, convert_bind_param).
-
- .. change::
- :tags: sql
- :tickets: 1068
-
- Temporarily rolled back the "ORDER BY" enhancement from. This feature is on hold pending further
- development.
-
- .. change::
- :tags: sql
- :tickets:
-
- The exists() construct won't "export" its contained list
- of elements as FROM clauses, allowing them to be used more
- effectively in the columns clause of a SELECT.
-
- .. change::
- :tags: sql
- :tickets: 798
-
- and_() and or_() now generate a ColumnElement, allowing
- boolean expressions as result columns, i.e.
- select([and_(1, 0)]).
-
- .. change::
- :tags: sql
- :tickets:
-
- Bind params now subclass ColumnElement which allows them to be
- selectable by orm.query (they already had most ColumnElement
- semantics).
-
- .. change::
- :tags: sql
- :tickets:
-
- Added select_from() method to exists() construct, which becomes
- more and more compatible with a regular select().
-
- .. change::
- :tags: sql
- :tickets: 1160
-
- Added func.min(), func.max(), func.sum() as "generic functions",
- which basically allows for their return type to be determined
- automatically. Helps with dates on SQLite, decimal types,
- others.
-
- .. change::
- :tags: sql
- :tickets:
-
- added decimal.Decimal as an "auto-detect" type; bind parameters
- and generic functions will set their type to Numeric when a
- Decimal is used.
-
- .. change::
- :tags: mysql
- :tickets:
-
- The 'length' argument to MSInteger, MSBigInteger, MSTinyInteger,
- MSSmallInteger and MSYear has been renamed to 'display_width'.
-
- .. change::
- :tags: mysql
- :tickets: 1146
-
- Added MSMediumInteger type.
-
- .. change::
- :tags: mysql
- :tickets:
-
- the function func.utc_timestamp() compiles to UTC_TIMESTAMP, without
- the parenthesis, which seem to get in the way when using in
- conjunction with executemany().
-
- .. change::
- :tags: oracle
- :tickets: 536
-
- limit/offset no longer uses ROW NUMBER OVER to limit rows,
- and instead uses subqueries in conjunction with a special
- Oracle optimization comment. Allows LIMIT/OFFSET to work
- in conjunction with DISTINCT.
-
- .. change::
- :tags: oracle
- :tickets: 1155
-
- has_sequence() now takes the current "schema" argument into
- account
-
- .. change::
- :tags: oracle
- :tickets: 1121
-
- added BFILE to reflected type names
-
-.. changelog::
- :version: 0.5.0beta3
- :released: Mon Aug 04 2008
-
- .. change::
- :tags: orm
- :tickets:
-
- The "entity_name" feature of SQLAlchemy mappers has been
- removed. For rationale, see https://tinyurl.com/6nm2ne
-
- .. change::
- :tags: orm
- :tickets:
-
- the "autoexpire" flag on Session, sessionmaker(), and
- scoped_session() has been renamed to "expire_on_commit". It
- does not affect the expiration behavior of rollback().
-
- .. change::
- :tags: orm
- :tickets:
-
- fixed endless loop bug which could occur within a mapper's
- deferred load of inherited attributes.
-
- .. change::
- :tags: orm
- :tickets:
-
- a legacy-support flag "_enable_transaction_accounting" flag
- added to Session which when False, disables all
- transaction-level object accounting, including expire on
- rollback, expire on commit, new/deleted list maintenance, and
- autoflush on begin.
-
- .. change::
- :tags: orm
- :tickets:
-
- The 'cascade' parameter to relation() accepts None as a value,
- which is equivalent to no cascades.
-
- .. change::
- :tags: orm
- :tickets:
-
- A critical fix to dynamic relations allows the "modified"
- history to be properly cleared after a flush().
-
- .. change::
- :tags: orm
- :tickets:
-
- user-defined @properties on a class are detected and left in
- place during mapper initialization. This means that a
- table-bound column of the same name will not be mapped at all
- if a @property is in the way (and the column is not remapped
- to a different name), nor will an instrumented attribute from
- an inherited class be applied. The same rules apply for names
- excluded using the include_properties/exclude_properties
- collections.
-
- .. change::
- :tags: orm
- :tickets:
-
- Added a new SessionExtension hook called after_attach(). This
- is called at the point of attachment for objects via add(),
- add_all(), delete(), and merge().
-
- .. change::
- :tags: orm
- :tickets: 1111
-
- A mapper which inherits from another, when inheriting the
- columns of its inherited mapper, will use any reassigned
- property names specified in that inheriting mapper.
- Previously, if "Base" had reassigned "base_id" to the name
- "id", "SubBase(Base)" would still get an attribute called
- "base_id". This could be worked around by explicitly stating
- the column in each submapper as well but this is fairly
- unworkable and also impossible when using declarative.
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed a series of potential race conditions in Session whereby
- asynchronous GC could remove unmodified, no longer referenced
- items from the session as they were present in a list of items
- to be processed, typically during session.expunge_all() and
- dependent methods.
-
- .. change::
- :tags: orm
- :tickets:
-
- Some improvements to the _CompileOnAttr mechanism which should
- reduce the probability of "Attribute x was not replaced during
- compile" warnings. (this generally applies to SQLA hackers,
- like Elixir devs).
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed bug whereby the "unsaved, pending instance" FlushError
- raised for a pending orphan would not take superclass mappers
- into account when generating the list of relations responsible
- for the error.
-
- .. change::
- :tags: sql
- :tickets:
-
- func.count() with no arguments renders as COUNT(*), equivalent
- to func.count(text('*')).
-
- .. change::
- :tags: sql
- :tickets: 1068
-
- simple label names in ORDER BY expressions render as
- themselves, and not as a re-statement of their corresponding
- expression. This feature is currently enabled only for
- SQLite, MySQL, and PostgreSQL. It can be enabled on other
- dialects as each is shown to support this
- behavior.
-
- .. change::
- :tags: ext
- :tickets:
-
- Class-bound attributes sent as arguments to relation()'s
- remote_side and foreign_keys parameters are now accepted,
- allowing them to be used with declarative. Additionally fixed
- bugs involving order_by being specified as a class-bound
- attribute in conjunction with eager loading.
-
- .. change::
- :tags: ext
- :tickets:
-
- declarative initialization of Columns adjusted so that
- non-renamed columns initialize in the same way as a non
- declarative mapper. This allows an inheriting mapper to set
- up its same-named "id" columns in particular such that the
- parent "id" column is favored over the child column, reducing
- database round trips when this value is requested.
-
- .. change::
- :tags: mysql
- :tickets: 1110
-
- Quoting of MSEnum values for use in CREATE TABLE is now
- optional & will be quoted on demand as required. (Quoting was
- always optional for use with existing tables.)
-
-.. changelog::
- :version: 0.5.0beta2
- :released: Mon Jul 14 2008
-
- .. change::
- :tags: orm
- :tickets: 870
-
- In addition to expired attributes, deferred attributes also
- load if their data is present in the result set.
-
- .. change::
- :tags: orm
- :tickets:
-
- session.refresh() raises an informative error message if the
- list of attributes does not include any column-based
- attributes.
-
- .. change::
- :tags: orm
- :tickets:
-
- query() raises an informative error message if no columns or
- mappers are specified.
-
- .. change::
- :tags: orm
- :tickets:
-
- lazy loaders now trigger autoflush before proceeding. This
- allows expire() of a collection or scalar relation to function
- properly in the context of autoflush.
-
- .. change::
- :tags: orm
- :tickets: 887
-
- column_property() attributes which represent SQL expressions
- or columns that are not present in the mapped tables (such as
- those from views) are automatically expired after an INSERT or
- UPDATE, assuming they have not been locally modified, so that
- they are refreshed with the most recent data upon access.
-
- .. change::
- :tags: orm
- :tickets: 1082
-
- Fixed explicit, self-referential joins between two
- joined-table inheritance mappers when using query.join(cls,
- aliased=True).
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed query.join() when used in conjunction with a
- columns-only clause and a SQL-expression ON clause in the
- join.
-
- .. change::
- :tags: orm
- :tickets:
-
- The "allow_column_override" flag from mapper() has been
- removed. This flag is virtually always misunderstood. Its
- specific functionality is available via the
- include_properties/exclude_properties mapper arguments.
-
- .. change::
- :tags: orm
- :tickets: 1066
-
- Repaired `__str__()` method on Query.
-
- .. change::
- :tags: orm
- :tickets:
-
- Session.bind gets used as a default even when table/mapper
- specific binds are defined.
-
- .. change::
- :tags: schema
- :tickets: 1075
-
- Added prefixes option to `Table` that accepts a list of
- strings to insert after CREATE in the CREATE TABLE statement.
-
- .. change::
- :tags: schema
- :tickets:
-
- Unicode, UnicodeText types now set "assert_unicode" and
- "convert_unicode" by default, but accept overriding
- \**kwargs for these values.
-
- .. change::
- :tags: sql
- :tickets:
-
- Added new match() operator that performs a full-text search.
- Supported on PostgreSQL, SQLite, MySQL, MS-SQL, and Oracle
- backends.
-
- .. change::
- :tags: sqlite
- :tickets: 1090
-
- Modified SQLite's representation of "microseconds" to match
- the output of str(somedatetime), i.e. in that the microseconds
- are represented as fractional seconds in string format. This
- makes SQLA's SQLite date type compatible with datetimes that
- were saved directly using Pysqlite (which just calls str()).
- Note that this is incompatible with the existing microseconds
- values in a SQLA 0.4 generated SQLite database file.
-
- To get the old behavior globally:
-
- from sqlalchemy.databases.sqlite import DateTimeMixin
- DateTimeMixin.__legacy_microseconds__ = True
-
- To get the behavior on individual DateTime types:
-
- t = sqlite.SLDateTime()
- t.__legacy_microseconds__ = True
-
- Then use "t" as the type on the Column.
-
- .. change::
- :tags: sqlite
- :tickets:
-
- SQLite Date, DateTime, and Time types only accept Python
- datetime objects now, not strings. If you'd like to format
- dates as strings yourself with SQLite, use a String type. If
- you'd like them to return datetime objects anyway despite
- their accepting strings as input, make a TypeDecorator around
- String - SQLA doesn't encourage this pattern.
-
- .. change::
- :tags: extensions
- :tickets: 1096
-
- Declarative supports a __table_args__ class variable, which is
- either a dictionary, or tuple of the form (arg1, arg2, ...,
- {kwarg1:value, ...}) which contains positional + kw arguments
- to be passed to the Table constructor.
-
-.. changelog::
- :version: 0.5.0beta1
- :released: Thu Jun 12 2008
-
- .. change::
- :tags:
- :tickets:
-
- The "__init__" trigger/decorator added by mapper now attempts
- to exactly mirror the argument signature of the original
- __init__. The pass-through for '_sa_session' is no longer
- implicit- you must allow for this keyword argument in your
- constructor.
-
- .. change::
- :tags:
- :tickets:
-
- ClassState is renamed to ClassManager.
-
- .. change::
- :tags:
- :tickets:
-
- Classes may supply their own InstrumentationManager by
- providing a __sa_instrumentation_manager__ property.
-
- .. change::
- :tags:
- :tickets:
-
- Custom instrumentation may use any mechanism to associate a
- ClassManager with a class and an InstanceState with an
- instance. Attributes on those objects are still the default
- association mechanism used by SQLAlchemy's native
- instrumentation.
-
- .. change::
- :tags:
- :tickets:
-
- Moved entity_name, _sa_session_id, and _instance_key from the
- instance object to the instance state. These values are still
- available in the old way, which is now deprecated, using
- descriptors attached to the class. A deprecation warning will
- be issued when accessed.
-
- .. change::
- :tags:
- :tickets:
-
- The _prepare_instrumentation alias for prepare_instrumentation
- has been removed.
-
- .. change::
- :tags:
- :tickets:
-
- sqlalchemy.exceptions has been renamed to sqlalchemy.exc. The
- module may be imported under either name.
-
- .. change::
- :tags:
- :tickets:
-
- ORM-related exceptions are now defined in sqlalchemy.orm.exc.
- ConcurrentModificationError, FlushError, and
- UnmappedColumnError compatibility aliases are installed in
- sqlalchemy.exc during the import of sqlalchemy.orm.
-
- .. change::
- :tags:
- :tickets:
-
- sqlalchemy.logging has been renamed to sqlalchemy.log.
-
- .. change::
- :tags:
- :tickets:
-
- The transitional sqlalchemy.log.SADeprecationWarning alias for
- the warning's definition in sqlalchemy.exc has been removed.
-
- .. change::
- :tags:
- :tickets:
-
- exc.AssertionError has been removed and usage replaced with
- Python's built-in AssertionError.
-
- .. change::
- :tags:
- :tickets:
-
- The behavior of MapperExtensions attached to multiple,
- entity_name= primary mappers for a single class has been
- altered. The first mapper() defined for a class is the only
- mapper eligible for the MapperExtension 'instrument_class',
- 'init_instance' and 'init_failed' events. This is backwards
- incompatible; previously the extensions of last mapper defined
- would receive these events.
-
- .. change::
- :tags: firebird
- :tickets:
-
- Added support for returning values from inserts (2.0+ only),
- updates and deletes (2.1+ only).
-
- .. change::
- :tags: general
- :tickets:
-
- global "propigate"->"propagate" change.
-
- .. change::
- :tags: orm
- :tickets:
-
- polymorphic_union() function respects the "key" of each
- Column if they differ from the column's name.
-
- .. change::
- :tags: orm
- :tickets: 1199
-
- Fixed 0.4-only bug preventing composite columns
- from working properly with inheriting mappers
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed RLock-related bug in mapper which could deadlock upon
- reentrant mapper compile() calls, something that occurs when
- using declarative constructs inside of ForeignKey objects.
- Ported from 0.5.
-
- .. change::
- :tags: orm
- :tickets: 1213
-
- Fixed bug in composite types which prevented a primary-key
- composite type from being mutated.
-
- .. change::
- :tags: orm
- :tickets: 976
-
- Added ScopedSession.is_active accessor.
-
- .. change::
- :tags: orm
- :tickets: 939
-
- Class-bound accessor can be used as the argument to
- relation() order_by.
-
- .. change::
- :tags: orm
- :tickets: 1072
-
- Fixed shard_id argument on ShardedSession.execute().
-
- .. change::
- :tags: sql
- :tickets: 1246
-
- Connection.invalidate() checks for closed status
- to avoid attribute errors.
-
- .. change::
- :tags: sql
- :tickets: 1094
-
- NullPool supports reconnect on failure behavior.
-
- .. change::
- :tags: sql
- :tickets: 1299
-
- The per-dialect cache used by TypeEngine to cache
- dialect-specific types is now a WeakKeyDictionary.
- This to prevent dialect objects from
- being referenced forever for an application that
- creates an arbitrarily large number of engines
- or dialects. There is a small performance penalty
- which will be resolved in 0.6.
-
- .. change::
- :tags: sql
- :tickets:
-
- Fixed SQLite reflection methods so that non-present
- cursor.description, which triggers an auto-cursor
- close, will be detected so that no results doesn't
- fail on recent versions of pysqlite which raise
- an error when fetchone() called with no rows present.
-
- .. change::
- :tags: postgres
- :tickets: 714
-
- Added Index reflection support to Postgres, using a
- great patch we long neglected, submitted by
- Ken Kuhlman.
-
- .. change::
- :tags: mysql
- :tickets: 1241
-
- Fixed bug in exception raise when FK columns not present
- during reflection.
-
- .. change::
- :tags: oracle
- :tickets: 1265
-
- Fixed bug which was preventing out params of certain types
- from being received; thanks a ton to huddlej at wwu.edu !
+++ /dev/null
-=============
-0.6 Changelog
-=============
-
-
-.. changelog::
- :version: 0.6.9
- :released: Sat May 05 2012
-
- .. change::
- :tags: general
- :tickets: 2279
-
- Adjusted the "importlater" mechanism, which is
- used internally to resolve import cycles,
- such that the usage of __import__ is completed
- when the import of sqlalchemy or sqlalchemy.orm
- is done, thereby avoiding any usage of __import__
- after the application starts new threads,
- fixes.
-
- .. change::
- :tags: orm
- :tickets: 2197
-
- Fixed bug whereby the source clause
- used by query.join() would be inconsistent
- if against a column expression that combined
- multiple entities together.
-
- .. change::
- :tags: orm, bug
- :tickets: 2310
-
- fixed inappropriate evaluation of user-mapped
- object in a boolean context within query.get().
-
- .. change::
- :tags: orm
- :tickets: 2228
-
- Fixed bug apparent only in Python 3 whereby
- sorting of persistent + pending objects during
- flush would produce an illegal comparison,
- if the persistent object primary key
- is not a single integer.
-
- .. change::
- :tags: orm
- :tickets: 2234
-
- Fixed bug where query.join() + aliased=True
- from a joined-inh structure to itself on
- relationship() with join condition on the child
- table would convert the lead entity into the
- joined one inappropriately.
-
- .. change::
- :tags: orm
- :tickets: 2287
-
- Fixed bug whereby mapper.order_by attribute would
- be ignored in the "inner" query within a
- subquery eager load. .
-
- .. change::
- :tags: orm
- :tickets: 2215
-
- Fixed bug whereby if a mapped class
- redefined __hash__() or __eq__() to something
- non-standard, which is a supported use case
- as SQLA should never consult these,
- the methods would be consulted if the class
- was part of a "composite" (i.e. non-single-entity)
- result set.
-
- .. change::
- :tags: orm
- :tickets: 2188
-
- Fixed subtle bug that caused SQL to blow
- up if: column_property() against subquery +
- joinedload + LIMIT + order by the column
- property() occurred. .
-
- .. change::
- :tags: orm
- :tickets: 2207
-
- The join condition produced by with_parent
- as well as when using a "dynamic" relationship
- against a parent will generate unique
- bindparams, rather than incorrectly repeating
- the same bindparam. .
-
- .. change::
- :tags: orm
- :tickets: 2199
-
- Repaired the "no statement condition"
- assertion in Query which would attempt
- to raise if a generative method were called
- after from_statement() were called..
-
- .. change::
- :tags: orm
- :tickets: 1776
-
- Cls.column.collate("some collation") now
- works.
-
- .. change::
- :tags: orm, bug
- :tickets: 2297
-
- Fixed the error formatting raised when
- a tuple is inadvertently passed to session.query().
-
- .. change::
- :tags: engine
- :tickets: 2317
-
- Backported the fix for introduced
- in 0.7.4, which ensures that the connection
- is in a valid state before attempting to call
- rollback()/prepare()/release() on savepoint
- and two-phase transactions.
-
- .. change::
- :tags: sql
- :tickets: 2188
-
- Fixed two subtle bugs involving column
- correspondence in a selectable,
- one with the same labeled subquery repeated, the other
- when the label has been "grouped" and
- loses itself. Affects.
-
- .. change::
- :tags: sql
- :tickets:
-
- Fixed bug whereby "warn on unicode" flag
- would get set for the String type
- when used with certain dialects. This
- bug is not in 0.7.
-
- .. change::
- :tags: sql
- :tickets: 2270
-
- Fixed bug whereby with_only_columns() method of
- Select would fail if a selectable were passed.. However, the FROM behavior is
- still incorrect here, so you need 0.7 in
- any case for this use case to be usable.
-
- .. change::
- :tags: schema
- :tickets:
-
- Added an informative error message when
- ForeignKeyConstraint refers to a column name in
- the parent that is not found.
-
- .. change::
- :tags: postgresql
- :tickets: 2291, 2141
-
- Fixed bug related to whereby the
- same modified index behavior in PG 9 affected
- primary key reflection on a renamed column..
-
- .. change::
- :tags: mysql
- :tickets: 2186
-
- Fixed OurSQL dialect to use ansi-neutral
- quote symbol "'" for XA commands instead
- of '"'. .
-
- .. change::
- :tags: mysql
- :tickets: 2225
-
- a CREATE TABLE will put the COLLATE option
- after CHARSET, which appears to be part of
- MySQL's arbitrary rules regarding if it will actually
- work or not.
-
- .. change::
- :tags: mssql, bug
- :tickets: 2269
-
- Decode incoming values when retrieving
- list of index names and the names of columns
- within those indexes.
-
- .. change::
- :tags: oracle
- :tickets: 2200
-
- Added ORA-00028 to disconnect codes, use
- cx_oracle _Error.code to get at the code,.
-
- .. change::
- :tags: oracle
- :tickets: 2220
-
- repaired the oracle.RAW type which did not
- generate the correct DDL.
-
- .. change::
- :tags: oracle
- :tickets: 2212
-
- added CURRENT to reserved word list.
-
- .. change::
- :tags: examples
- :tickets: 2266
-
- Adjusted dictlike-polymorphic.py example
- to apply the CAST such that it works on
- PG, other databases.
-
-.. changelog::
- :version: 0.6.8
- :released: Sun Jun 05 2011
-
- .. change::
- :tags: orm
- :tickets: 2144
-
- Calling query.get() against a column-based entity is
- invalid, this condition now raises a deprecation warning.
-
- .. change::
- :tags: orm
- :tickets: 2151
-
- a non_primary mapper will inherit the _identity_class
- of the primary mapper. This so that a non_primary
- established against a class that's normally in an
- inheritance mapping will produce results that are
- identity-map compatible with that of the primary
- mapper
-
- .. change::
- :tags: orm
- :tickets: 2148
-
- Backported 0.7's identity map implementation, which
- does not use a mutex around removal. This as some users
- were still getting deadlocks despite the adjustments
- in 0.6.7; the 0.7 approach that doesn't use a mutex
- does not appear to produce "dictionary changed size"
- issues, the original rationale for the mutex.
-
- .. change::
- :tags: orm
- :tickets: 2163
-
- Fixed the error message emitted for "can't
- execute syncrule for destination column 'q';
- mapper 'X' does not map this column" to
- reference the correct mapper. .
-
- .. change::
- :tags: orm
- :tickets: 2149
-
- Fixed bug where determination of "self referential"
- relationship would fail with no workaround
- for joined-inh subclass related to itself,
- or joined-inh subclass related to a subclass
- of that with no cols in the sub-sub class
- in the join condition.
-
- .. change::
- :tags: orm
- :tickets: 2153
-
- mapper() will ignore non-configured foreign keys
- to unrelated tables when determining inherit
- condition between parent and child class.
- This is equivalent to behavior already
- applied to declarative. Note that 0.7 has a
- more comprehensive solution to this, altering
- how join() itself determines an FK error.
-
- .. change::
- :tags: orm
- :tickets: 2171
-
- Fixed bug whereby mapper mapped to an anonymous
- alias would fail if logging were used, due to
- unescaped % sign in the alias name.
-
- .. change::
- :tags: orm
- :tickets: 2170
-
- Modify the text of the message which occurs
- when the "identity" key isn't detected on
- flush, to include the common cause that
- the Column isn't set up to detect
- auto-increment correctly;.
-
- .. change::
- :tags: orm
- :tickets: 2182
-
- Fixed bug where transaction-level "deleted"
- collection wouldn't be cleared of expunged
- states, raising an error if they later
- became transient.
-
- .. change::
- :tags: sql
- :tickets: 2147
-
- Fixed bug whereby if FetchedValue was passed
- to column server_onupdate, it would not
- have its parent "column" assigned, added
- test coverage for all column default assignment
- patterns.
-
- .. change::
- :tags: sql
- :tickets: 2167
-
- Fixed bug whereby nesting a label of a select()
- with another label in it would produce incorrect
- exported columns. Among other things this would
- break an ORM column_property() mapping against
- another column_property(). .
-
- .. change::
- :tags: engine
- :tickets: 2178
-
- Adjusted the __contains__() method of
- a RowProxy result row such that no exception
- throw is generated internally;
- NoSuchColumnError() also will generate its
- message regardless of whether or not the column
- construct can be coerced to a string..
-
- .. change::
- :tags: postgresql
- :tickets: 2141
-
- Fixed bug affecting PG 9 whereby index reflection
- would fail if against a column whose name
- had changed. .
-
- .. change::
- :tags: postgresql
- :tickets: 2175
-
- Some unit test fixes regarding numeric arrays,
- MATCH operator. A potential floating-point
- inaccuracy issue was fixed, and certain tests
- of the MATCH operator only execute within an
- EN-oriented locale for now. .
-
- .. change::
- :tags: mssql
- :tickets: 2169
-
- Fixed bug in MSSQL dialect whereby the aliasing
- applied to a schema-qualified table would leak
- into enclosing select statements.
-
- .. change::
- :tags: mssql
- :tickets: 2159
-
- Fixed bug whereby DATETIME2 type would fail on
- the "adapt" step when used in result sets or
- bound parameters. This issue is not in 0.7.
-
-.. changelog::
- :version: 0.6.7
- :released: Wed Apr 13 2011
-
- .. change::
- :tags: orm
- :tickets: 2087
-
- Tightened the iterate vs. remove mutex around the
- identity map iteration, attempting to reduce the
- chance of an (extremely rare) reentrant gc operation
- causing a deadlock. Might remove the mutex in
- 0.7.
-
- .. change::
- :tags: orm
- :tickets: 2030
-
- Added a `name` argument to `Query.subquery()`, to allow
- a fixed name to be assigned to the alias object.
-
- .. change::
- :tags: orm
- :tickets: 2019
-
- A warning is emitted when a joined-table inheriting mapper
- has no primary keys on the locally mapped table
- (but has pks on the superclass table).
-
- .. change::
- :tags: orm
- :tickets: 2038
-
- Fixed bug where "middle" class in a polymorphic hierarchy
- would have no 'polymorphic_on' column if it didn't also
- specify a 'polymorphic_identity', leading to strange
- errors upon refresh, wrong class loaded when querying
- from that target. Also emits the correct WHERE criterion
- when using single table inheritance.
-
- .. change::
- :tags: orm
- :tickets: 1995
-
- Fixed bug where a column with a SQL or server side default
- that was excluded from a mapping with include_properties
- or exclude_properties would result in UnmappedColumnError.
-
- .. change::
- :tags: orm
- :tickets: 2046
-
- A warning is emitted in the unusual case that an
- append or similar event on a collection occurs after
- the parent object has been dereferenced, which
- prevents the parent from being marked as "dirty"
- in the session. This will be an exception in 0.7.
-
- .. change::
- :tags: orm
- :tickets: 2098
-
- Fixed bug in query.options() whereby a path
- applied to a lazyload using string keys could
- overlap a same named attribute on the wrong
- entity. Note 0.7 has an updated version of this
- fix.
-
- .. change::
- :tags: orm
- :tickets: 2063
-
- Reworded the exception raised when a flush
- is attempted of a subclass that is not polymorphic
- against the supertype.
-
- .. change::
- :tags: orm
- :tickets: 2123
-
- Some fixes to the state handling regarding
- backrefs, typically when autoflush=False, where
- the back-referenced collection wouldn't
- properly handle add/removes with no net
- change. Thanks to Richard Murri for the
- test case + patch.
-
- .. change::
- :tags: orm
- :tickets: 2130
-
- a "having" clause would be copied from the
- inside to the outside query if from_self()
- were used..
-
- .. change::
- :tags: sql
- :tickets: 2028
-
- Column.copy(), as used in table.tometadata(), copies the
- 'doc' attribute.
-
- .. change::
- :tags: sql
- :tickets: 2023
-
- Added some defs to the resultproxy.c extension so that
- the extension compiles and runs on Python 2.4.
-
- .. change::
- :tags: sql
- :tickets: 2042
-
- The compiler extension now supports overriding the default
- compilation of expression._BindParamClause including that
- the auto-generated binds within the VALUES/SET clause
- of an insert()/update() statement will also use the new
- compilation rules.
-
- .. change::
- :tags: sql
- :tickets: 2089
-
- Added accessors to ResultProxy "returns_rows", "is_insert"
-
- .. change::
- :tags: sql
- :tickets: 2116
-
- The limit/offset keywords to select() as well
- as the value passed to select.limit()/offset()
- will be coerced to integer.
-
- .. change::
- :tags: engine
- :tickets: 2102
-
- Fixed bug in QueuePool, SingletonThreadPool whereby
- connections that were discarded via overflow or periodic
- cleanup() were not explicitly closed, leaving garbage
- collection to the task instead. This generally only
- affects non-reference-counting backends like Jython
- and PyPy. Thanks to Jaimy Azle for spotting
- this.
-
- .. change::
- :tags: sqlite
- :tickets: 2115
-
- Fixed bug where reflection of foreign key
- created as "REFERENCES <tablename>" without
- col name would fail.
-
- .. change::
- :tags: postgresql
- :tickets: 1083
-
- When explicit sequence execution derives the name
- of the auto-generated sequence of a SERIAL column,
- which currently only occurs if implicit_returning=False,
- now accommodates if the table + column name is greater
- than 63 characters using the same logic PostgreSQL uses.
-
- .. change::
- :tags: postgresql
- :tickets: 2044
-
- Added an additional libpq message to the list of "disconnect"
- exceptions, "could not receive data from server"
-
- .. change::
- :tags: postgresql
- :tickets: 2092
-
- Added RESERVED_WORDS for postgresql dialect.
-
- .. change::
- :tags: postgresql
- :tickets: 2073
-
- Fixed the BIT type to allow a "length" parameter, "varying"
- parameter. Reflection also fixed.
-
- .. change::
- :tags: informix
- :tickets: 2092
-
- Added RESERVED_WORDS informix dialect.
-
- .. change::
- :tags: mssql
- :tickets: 2071
-
- Rewrote the query used to get the definition of a view,
- typically when using the Inspector interface, to
- use sys.sql_modules instead of the information schema,
- thereby allowing views definitions longer than 4000
- characters to be fully returned.
-
- .. change::
- :tags: mysql
- :tickets: 2047
-
- oursql dialect accepts the same "ssl" arguments in
- create_engine() as that of MySQLdb.
-
- .. change::
- :tags: firebird
- :tickets: 2083
-
- The "implicit_returning" flag on create_engine() is
- honored if set to False.
-
- .. change::
- :tags: oracle
- :tickets: 2100
-
- Using column names that would require quotes
- for the column itself or for a name-generated
- bind parameter, such as names with special
- characters, underscores, non-ascii characters,
- now properly translate bind parameter keys when
- talking to cx_oracle.
-
- .. change::
- :tags: oracle
- :tickets: 2116
-
- Oracle dialect adds use_binds_for_limits=False
- create_engine() flag, will render the LIMIT/OFFSET
- values inline instead of as binds, reported to
- modify the execution plan used by Oracle.
-
- .. change::
- :tags: ext
- :tickets: 2090
-
- The horizontal_shard ShardedSession class accepts the common
- Session argument "query_cls" as a constructor argument,
- to enable further subclassing of ShardedQuery.
-
- .. change::
- :tags: declarative
- :tickets: 2050
-
- Added an explicit check for the case that the name
- 'metadata' is used for a column attribute on a
- declarative class.
-
- .. change::
- :tags: declarative
- :tickets: 2061
-
- Fix error message referencing old @classproperty
- name to reference @declared_attr
-
- .. change::
- :tags: declarative
- :tickets: 2091
-
- Arguments in __mapper_args__ that aren't "hashable"
- aren't mistaken for always-hashable, possibly-column
- arguments.
-
- .. change::
- :tags: documentation
- :tickets: 2029
-
- Documented SQLite DATE/TIME/DATETIME types.
-
- .. change::
- :tags: examples
- :tickets: 2090
-
- The Beaker caching example allows a "query_cls" argument
- to the query_callable() function.
-
-.. changelog::
- :version: 0.6.6
- :released: Sat Jan 08 2011
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed bug whereby a non-"mutable" attribute modified event
- which occurred on an object that was clean except for
- preceding mutable attribute changes would fail to strongly
- reference itself in the identity map. This would cause the
- object to be garbage collected, losing track of any changes
- that weren't previously saved in the "mutable changes"
- dictionary.
-
- .. change::
- :tags: orm
- :tickets: 2013
-
- Fixed bug whereby "passive_deletes='all'" wasn't passing
- the correct symbols to lazy loaders during flush, thereby
- causing an unwarranted load.
-
- .. change::
- :tags: orm
- :tickets: 1997
-
- Fixed bug which prevented composite mapped
- attributes from being used on a mapped select statement.. Note the workings of composite are slated to
- change significantly in 0.7.
-
- .. change::
- :tags: orm
- :tickets: 1976
-
- active_history flag also added to composite().
- The flag has no effect in 0.6, but is instead
- a placeholder flag for forwards compatibility,
- as it applies in 0.7 for composites.
-
- .. change::
- :tags: orm
- :tickets: 2002
-
- Fixed uow bug whereby expired objects passed to
- Session.delete() would not have unloaded references
- or collections taken into account when deleting
- objects, despite passive_deletes remaining at
- its default of False.
-
- .. change::
- :tags: orm
- :tickets: 1987
-
- A warning is emitted when version_id_col is specified
- on an inheriting mapper when the inherited mapper
- already has one, if those column expressions are not
- the same.
-
- .. change::
- :tags: orm
- :tickets: 1954
-
- "innerjoin" flag doesn't take effect along the chain
- of joinedload() joins if a previous join in that chain
- is an outer join, thus allowing primary rows without
- a referenced child row to be correctly returned
- in results.
-
- .. change::
- :tags: orm
- :tickets: 1964
-
- Fixed bug regarding "subqueryload" strategy whereby
- strategy would fail if the entity was an aliased()
- construct.
-
- .. change::
- :tags: orm
- :tickets: 2014
-
- Fixed bug regarding "subqueryload" strategy whereby
- the join would fail if using a multi-level load
- of the form from A->joined-subclass->C
-
- .. change::
- :tags: orm
- :tickets: 1968
-
- Fixed indexing of Query objects by -1. It was erroneously
- transformed to the empty slice -1:0 that resulted in
- IndexError.
-
- .. change::
- :tags: orm
- :tickets: 1971
-
- The mapper argument "primary_key" can be passed as a
- single column as well as a list or tuple.
- The documentation examples that illustrated it as a
- scalar value have been changed to lists.
-
- .. change::
- :tags: orm
- :tickets: 1961
-
- Added active_history flag to relationship()
- and column_property(), forces attribute events to
- always load the "old" value, so that it's available to
- attributes.get_history().
-
- .. change::
- :tags: orm
- :tickets: 1977
-
- Query.get() will raise if the number of params
- in a composite key is too large, as well as too
- small.
-
- .. change::
- :tags: orm
- :tickets: 1992
-
- Backport of "optimized get" fix from 0.7,
- improves the generation of joined-inheritance
- "load expired row" behavior.
-
- .. change::
- :tags: orm
- :tickets:
-
- A little more verbiage to the "primaryjoin" error,
- in an unusual condition that the join condition
- "works" for viewonly but doesn't work for non-viewonly,
- and foreign_keys wasn't used - adds "foreign_keys" to
- the suggestion. Also add "foreign_keys" to the
- suggestion for the generic "direction" error.
-
- .. change::
- :tags: sql
- :tickets: 1984
-
- Fixed operator precedence rules for multiple
- chains of a single non-associative operator.
- I.e. "x - (y - z)" will compile as "x - (y - z)"
- and not "x - y - z". Also works with labels,
- i.e. "x - (y - z).label('foo')"
-
- .. change::
- :tags: sql
- :tickets: 1967
-
- The 'info' attribute of Column is copied during
- Column.copy(), i.e. as occurs when using columns
- in declarative mixins.
-
- .. change::
- :tags: sql
- :tickets:
-
- Added a bind processor for booleans which coerces
- to int, for DBAPIs such as pymssql that naively call
- str() on values.
-
- .. change::
- :tags: sql
- :tickets: 2000
-
- CheckConstraint will copy its 'initially', 'deferrable',
- and '_create_rule' attributes within a copy()/tometadata()
-
- .. change::
- :tags: engine
- :tickets:
-
- The "unicode warning" against non-unicode bind data
- is now raised only when the
- Unicode type is used explicitly; not when
- convert_unicode=True is used on the engine
- or String type.
-
- .. change::
- :tags: engine
- :tickets: 1978
-
- Fixed memory leak in C version of Decimal result
- processor.
-
- .. change::
- :tags: engine
- :tickets: 1871
-
- Implemented sequence check capability for the C
- version of RowProxy, as well as 2.7 style
- "collections.Sequence" registration for RowProxy.
-
- .. change::
- :tags: engine
- :tickets: 1998
-
- Threadlocal engine methods rollback(), commit(),
- prepare() won't raise if no transaction is in progress;
- this was a regression introduced in 0.6.
-
- .. change::
- :tags: engine
- :tickets: 2004
-
- Threadlocal engine returns itself upon begin(),
- begin_nested(); engine then implements contextmanager
- methods to allow the "with" statement.
-
- .. change::
- :tags: postgresql
- :tickets: 1984
-
- Single element tuple expressions inside an IN clause
- parenthesize correctly, also from
-
- .. change::
- :tags: postgresql
- :tickets: 1955
-
- Ensured every numeric, float, int code, scalar + array,
- are recognized by psycopg2 and pg8000's "numeric"
- base type.
-
- .. change::
- :tags: postgresql
- :tickets: 1956
-
- Added as_uuid=True flag to the UUID type, will receive
- and return values as Python UUID() objects rather than
- strings. Currently, the UUID type is only known to
- work with psycopg2.
-
- .. change::
- :tags: postgresql
- :tickets: 1989
-
- Fixed bug whereby KeyError would occur with non-ENUM
- supported PG versions after a pool dispose+recreate
- would occur.
-
- .. change::
- :tags: mysql
- :tickets: 1960
-
- Fixed error handling for Jython + zxjdbc, such that
- has_table() property works again. Regression from
- 0.6.3 (we don't have a Jython buildbot, sorry)
-
- .. change::
- :tags: sqlite
- :tickets: 1851
-
- The REFERENCES clause in a CREATE TABLE that includes
- a remote schema to another table with the same schema
- name now renders the remote name without
- the schema clause, as required by SQLite.
-
- .. change::
- :tags: sqlite
- :tickets:
-
- On the same theme, the REFERENCES clause in a CREATE TABLE
- that includes a remote schema to a *different* schema
- than that of the parent table doesn't render at all,
- as cross-schema references do not appear to be supported.
-
- .. change::
- :tags: mssql
- :tickets: 1770
-
- The rewrite of index reflection in was
- unfortunately not tested correctly, and returned incorrect
- results. This regression is now fixed.
-
- .. change::
- :tags: oracle
- :tickets: 1953
-
- The cx_oracle "decimal detection" logic, which takes place
- for result set columns with ambiguous numeric characteristics,
- now uses the decimal point character determined by the locale/
- NLS_LANG setting, using an on-first-connect detection of
- this character. cx_oracle 5.0.3 or greater is also required
- when using a non-period-decimal-point NLS_LANG setting..
-
- .. change::
- :tags: firebird
- :tickets: 2012
-
- Firebird numeric type now checks for Decimal explicitly,
- lets float() pass right through, thereby allowing
- special values such as float('inf').
-
- .. change::
- :tags: declarative
- :tickets: 1972
-
- An error is raised if __table_args__ is not in tuple
- or dict format, and is not None.
-
- .. change::
- :tags: sqlsoup
- :tickets: 1975
-
- Added "map_to()" method to SqlSoup, which is a "master"
- method which accepts explicit arguments for each aspect of
- the selectable and mapping, including a base class per
- mapping.
-
- .. change::
- :tags: sqlsoup
- :tickets:
-
- Mapped selectables used with the map(), with_labels(),
- join() methods no longer put the given argument into the
- internal "cache" dictionary. Particularly since the
- join() and select() objects are created in the method
- itself this was pretty much a pure memory leaking behavior.
-
- .. change::
- :tags: examples
- :tickets:
-
- The versioning example now supports detection of changes
- in an associated relationship().
-
-.. changelog::
- :version: 0.6.5
- :released: Sun Oct 24 2010
-
- .. change::
- :tags: orm
- :tickets: 1914
-
- Added a new "lazyload" option "immediateload".
- Issues the usual "lazy" load operation automatically
- as the object is populated. The use case
- here is when loading objects to be placed in
- an offline cache, or otherwise used after
- the session isn't available, and straight 'select'
- loading, not 'joined' or 'subquery', is desired.
-
- .. change::
- :tags: orm
- :tickets: 1920
-
- New Query methods: query.label(name), query.as_scalar(),
- return the query's statement as a scalar subquery
- with /without label;
- query.with_entities(\*ent), replaces the SELECT list of
- the query with new entities.
- Roughly equivalent to a generative form of query.values()
- which accepts mapped entities as well as column
- expressions.
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed recursion bug which could occur when moving
- an object from one reference to another, with
- backrefs involved, where the initiating parent
- was a subclass (with its own mapper) of the
- previous parent.
-
- .. change::
- :tags: orm
- :tickets: 1918
-
- Fixed a regression in 0.6.4 which occurred if you
- passed an empty list to "include_properties" on
- mapper()
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed labeling bug in Query whereby the NamedTuple
- would mis-apply labels if any of the column
- expressions were un-labeled.
-
- .. change::
- :tags: orm
- :tickets: 1925
-
- Patched a case where query.join() would adapt the
- right side to the right side of the left's join
- inappropriately
-
- .. change::
- :tags: orm
- :tickets:
-
- Query.select_from() has been beefed up to help
- ensure that a subsequent call to query.join()
- will use the select_from() entity, assuming it's
- a mapped entity and not a plain selectable,
- as the default "left" side, not the first entity
- in the Query object's list of entities.
-
- .. change::
- :tags: orm
- :tickets:
-
- The exception raised by Session when it is used
- subsequent to a subtransaction rollback (which is what
- happens when a flush fails in autocommit=False mode) has
- now been reworded (this is the "inactive due to a
- rollback in a subtransaction" message). In particular,
- if the rollback was due to an exception during flush(),
- the message states this is the case, and reiterates the
- string form of the original exception that occurred
- during flush. If the session is closed due to explicit
- usage of subtransactions (not very common), the message
- just states this is the case.
-
- .. change::
- :tags: orm
- :tickets:
-
- The exception raised by Mapper when repeated requests to
- its initialization are made after initialization already
- failed no longer assumes the "hasattr" case, since
- there's other scenarios in which this message gets
- emitted, and the message also does not compound onto
- itself multiple times - you get the same message for
- each attempt at usage. The misnomer "compiles" is being
- traded out for "initialize".
-
- .. change::
- :tags: orm
- :tickets: 1935
-
- Fixed bug in query.update() where 'evaluate' or 'fetch'
- expiration would fail if the column expression key was
- a class attribute with a different keyname as the
- actual column name.
-
- .. change::
- :tags: orm
- :tickets:
-
- Added an assertion during flush which ensures
- that no NULL-holding identity keys were generated
- on "newly persistent" objects.
- This can occur when user defined code inadvertently
- triggers flushes on not-fully-loaded objects.
-
- .. change::
- :tags: orm
- :tickets: 1910
-
- lazy loads for relationship attributes now use
- the current state, not the "committed" state,
- of foreign and primary key attributes
- when issuing SQL, if a flush is not in process.
- Previously, only the database-committed state would
- be used. In particular, this would cause a many-to-one
- get()-on-lazyload operation to fail, as autoflush
- is not triggered on these loads when the attributes are
- determined and the "committed" state may not be
- available.
-
- .. change::
- :tags: orm
- :tickets:
-
- A new flag on relationship(), load_on_pending, allows
- the lazy loader to fire off on pending objects without a
- flush taking place, as well as a transient object that's
- been manually "attached" to the session. Note that this
- flag blocks attribute events from taking place when an
- object is loaded, so backrefs aren't available until
- after a flush. The flag is only intended for very
- specific use cases.
-
- .. change::
- :tags: orm
- :tickets:
-
- Another new flag on relationship(), cascade_backrefs,
- disables the "save-update" cascade when the event was
- initiated on the "reverse" side of a bidirectional
- relationship. This is a cleaner behavior so that
- many-to-ones can be set on a transient object without
- it getting sucked into the child object's session,
- while still allowing the forward collection to
- cascade. We *might* default this to False in 0.7.
-
- .. change::
- :tags: orm
- :tickets:
-
- Slight improvement to the behavior of
- "passive_updates=False" when placed only on the
- many-to-one side of a relationship; documentation has
- been clarified that passive_updates=False should really
- be on the one-to-many side.
-
- .. change::
- :tags: orm
- :tickets:
-
- Placing passive_deletes=True on a many-to-one emits
- a warning, since you probably intended to put it on
- the one-to-many side.
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed bug that would prevent "subqueryload" from
- working correctly with single table inheritance
- for a relationship from a subclass - the "where
- type in (x, y, z)" only gets placed on the inside,
- instead of repeatedly.
-
- .. change::
- :tags: orm
- :tickets:
-
- When using from_self() with single table inheritance,
- the "where type in (x, y, z)" is placed on the outside
- of the query only, instead of repeatedly. May make
- some more adjustments to this.
-
- .. change::
- :tags: orm
- :tickets: 1924
-
- scoped_session emits a warning when configure() is
- called if a Session is already present (checks only the
- current thread)
-
- .. change::
- :tags: orm
- :tickets: 1932
-
- reworked the internals of mapper.cascade_iterator() to
- cut down method calls by about 9% in some circumstances.
-
- .. change::
- :tags: sql
- :tickets:
-
- Fixed bug in TypeDecorator whereby the dialect-specific
- type was getting pulled in to generate the DDL for a
- given type, which didn't always return the correct result.
-
- .. change::
- :tags: sql
- :tickets:
-
- TypeDecorator can now have a fully constructed type
- specified as its "impl", in addition to a type class.
-
- .. change::
- :tags: sql
- :tickets:
-
- TypeDecorator will now place itself as the resulting
- type for a binary expression where the type coercion
- rules would normally return its impl type - previously,
- a copy of the impl type would be returned which would
- have the TypeDecorator embedded into it as the "dialect"
- impl, this was probably an unintentional way of achieving
- the desired effect.
-
- .. change::
- :tags: sql
- :tickets:
-
- TypeDecorator.load_dialect_impl() returns "self.impl" by
- default, i.e. not the dialect implementation type of
- "self.impl". This to support compilation correctly.
- Behavior can be user-overridden in exactly the same way
- as before to the same effect.
-
- .. change::
- :tags: sql
- :tickets:
-
- Added type_coerce(expr, type\_) expression element.
- Treats the given expression as the given type when evaluating
- expressions and processing result rows, but does not
- affect the generation of SQL, other than an anonymous
- label.
-
- .. change::
- :tags: sql
- :tickets:
-
- Table.tometadata() now copies Index objects associated
- with the Table as well.
-
- .. change::
- :tags: sql
- :tickets:
-
- Table.tometadata() issues a warning if the given Table
- is already present in the target MetaData - the existing
- Table object is returned.
-
- .. change::
- :tags: sql
- :tickets:
-
- An informative error message is raised if a Column
- which has not yet been assigned a name, i.e. as in
- declarative, is used in a context where it is
- exported to the columns collection of an enclosing
- select() construct, or if any construct involving
- that column is compiled before its name is
- assigned.
-
- .. change::
- :tags: sql
- :tickets: 1862
-
- as_scalar(), label() can be called on a selectable
- which contains a Column that is not yet named.
-
- .. change::
- :tags: sql
- :tickets: 1907
-
- Fixed recursion overflow which could occur when operating
- with two expressions both of type "NullType", but
- not the singleton NULLTYPE instance.
-
- .. change::
- :tags: declarative
- :tickets: 1922
-
- @classproperty (soon/now @declared_attr) takes effect for
- __mapper_args__, __table_args__, __tablename__ on
- a base class that is not a mixin, as well as mixins.
-
- .. change::
- :tags: declarative
- :tickets: 1915
-
- @classproperty 's official name/location for usage
- with declarative is sqlalchemy.ext.declarative.declared_attr.
- Same thing, but moving there since it is more of a
- "marker" that's specific to declarative,
- not just an attribute technique.
-
- .. change::
- :tags: declarative
- :tickets: 1931, 1930
-
- Fixed bug whereby columns on a mixin wouldn't propagate
- correctly to a single-table, or joined-table,
- inheritance scheme where the attribute name is
- different than that of the column.,.
-
- .. change::
- :tags: declarative
- :tickets:
-
- A mixin can now specify a column that overrides
- a column of the same name associated with a superclass.
- Thanks to Oystein Haaland.
-
- .. change::
- :tags: engine
- :tickets:
-
- Fixed a regression in 0.6.4 whereby the change that
- allowed cursor errors to be raised consistently broke
- the result.lastrowid accessor. Test coverage has
- been added for result.lastrowid. Note that lastrowid
- is only supported by Pysqlite and some MySQL drivers,
- so isn't super-useful in the general case.
-
- .. change::
- :tags: engine
- :tickets:
-
- the logging message emitted by the engine when
- a connection is first used is now "BEGIN (implicit)"
- to emphasize that DBAPI has no explicit begin().
-
- .. change::
- :tags: engine
- :tickets: 1936
-
- added "views=True" option to metadata.reflect(),
- will add the list of available views to those
- being reflected.
-
- .. change::
- :tags: engine
- :tickets: 1899
-
- engine_from_config() now accepts 'debug' for
- 'echo', 'echo_pool', 'force' for 'convert_unicode',
- boolean values for 'use_native_unicode'.
-
- .. change::
- :tags: postgresql
- :tickets:
-
- Added "as_tuple" flag to ARRAY type, returns results
- as tuples instead of lists to allow hashing.
-
- .. change::
- :tags: postgresql
- :tickets: 1933
-
- Fixed bug which prevented "domain" built from a
- custom type such as "enum" from being reflected.
-
- .. change::
- :tags: mysql
- :tickets: 1940
-
- Fixed bug involving reflection of CURRENT_TIMESTAMP
- default used with ON UPDATE clause, thanks to
- Taavi Burns
-
- .. change::
- :tags: oracle
- :tickets: 1878
-
- The implicit_returning argument to create_engine()
- is now honored regardless of detected version of
- Oracle. Previously, the flag would be forced
- to False if server version info was < 10.
-
- .. change::
- :tags: mssql
- :tickets: 1946
-
- Fixed reflection bug which did not properly handle
- reflection of unknown types.
-
- .. change::
- :tags: mssql
- :tickets: 1943
-
- Fixed bug where aliasing of tables with "schema" would
- fail to compile properly.
-
- .. change::
- :tags: mssql
- :tickets: 1770
-
- Rewrote the reflection of indexes to use sys.
- catalogs, so that column names of any configuration
- (spaces, embedded commas, etc.) can be reflected.
- Note that reflection of indexes requires SQL
- Server 2005 or greater.
-
- .. change::
- :tags: mssql
- :tickets: 1952
-
- mssql+pymssql dialect now honors the "port" portion
- of the URL instead of discarding it.
-
- .. change::
- :tags: informix
- :tickets: 1906
-
- *Major* cleanup / modernization of the Informix
- dialect for 0.6, courtesy Florian Apolloner.
-
- .. change::
- :tags: tests
- :tickets:
-
- the NoseSQLAlchemyPlugin has been moved to a
- new package "sqlalchemy_nose" which installs
- along with "sqlalchemy". This so that the "nosetests"
- script works as always but also allows the
- --with-coverage option to turn on coverage before
- SQLAlchemy modules are imported, allowing coverage
- to work correctly.
-
- .. change::
- :tags: misc
- :tickets: 1890
-
- CircularDependencyError now has .cycles and .edges
- members, which are the set of elements involved in
- one or more cycles, and the set of edges as 2-tuples.
-
-.. changelog::
- :version: 0.6.4
- :released: Tue Sep 07 2010
-
- .. change::
- :tags: orm
- :tickets:
-
- The name ConcurrentModificationError has been
- changed to StaleDataError, and descriptive
- error messages have been revised to reflect
- exactly what the issue is. Both names will
- remain available for the foreseeable future
- for schemes that may be specifying
- ConcurrentModificationError in an "except:"
- clause.
-
- .. change::
- :tags: orm
- :tickets: 1891
-
- Added a mutex to the identity map which mutexes
- remove operations against iteration methods,
- which now pre-buffer before returning an
- iterable. This because asynchronous gc
- can remove items via the gc thread at any time.
-
- .. change::
- :tags: orm
- :tickets:
-
- The Session class is now present in sqlalchemy.orm.*.
- We're moving away from the usage of create_session(),
- which has non-standard defaults, for those situations
- where a one-step Session constructor is desired. Most
- users should stick with sessionmaker() for general use,
- however.
-
- .. change::
- :tags: orm
- :tickets:
-
- query.with_parent() now accepts transient objects
- and will use the non-persistent values of their pk/fk
- attributes in order to formulate the criterion.
- Docs are also clarified as to the purpose of with_parent().
-
- .. change::
- :tags: orm
- :tickets:
-
- The include_properties and exclude_properties arguments
- to mapper() now accept Column objects as members in
- addition to strings. This so that same-named Column
- objects, such as those within a join(), can be
- disambiguated.
-
- .. change::
- :tags: orm
- :tickets: 1896
-
- A warning is now emitted if a mapper is created against a
- join or other single selectable that includes multiple
- columns with the same name in its .c. collection,
- and those columns aren't explicitly named as part of
- the same or separate attributes (or excluded).
- In 0.7 this warning will be an exception. Note that
- this warning is not emitted when the combination occurs
- as a result of inheritance, so that attributes
- still allow being overridden naturally.. In 0.7 this will be improved further.
-
- .. change::
- :tags: orm
- :tickets: 1896
-
- The primary_key argument to mapper() can now specify
- a series of columns that are only a subset of
- the calculated "primary key" columns of the mapped
- selectable, without an error being raised. This
- helps for situations where a selectable's effective
- primary key is simpler than the number of columns
- in the selectable that are actually marked as
- "primary_key", such as a join against two
- tables on their primary key columns.
-
- .. change::
- :tags: orm
- :tickets:
-
- An object that's been deleted now gets a flag
- 'deleted', which prohibits the object from
- being re-add()ed to the session, as previously
- the object would live in the identity map
- silently until its attributes were accessed.
- The make_transient() function now resets this
- flag along with the "key" flag.
-
- .. change::
- :tags: orm
- :tickets:
-
- make_transient() can be safely called on an
- already transient instance.
-
- .. change::
- :tags: orm
- :tickets:
-
- a warning is emitted in mapper() if the polymorphic_on
- column is not present either in direct or derived
- form in the mapped selectable or in the
- with_polymorphic selectable, instead of silently
- ignoring it. Look for this to become an
- exception in 0.7.
-
- .. change::
- :tags: orm
- :tickets:
-
- Another pass through the series of error messages
- emitted when relationship() is configured with
- ambiguous arguments. The "foreign_keys"
- setting is no longer mentioned, as it is almost
- never needed and it is preferable users set up
- correct ForeignKey metadata, which is now the
- recommendation. If 'foreign_keys'
- is used and is incorrect, the message suggests
- the attribute is probably unnecessary. Docs
- for the attribute are beefed up. This
- because all confused relationship() users on the
- ML appear to be attempting to use foreign_keys
- due to the message, which only confuses them
- further since Table metadata is much clearer.
-
- .. change::
- :tags: orm
- :tickets: 1877
-
- If the "secondary" table has no ForeignKey metadata
- and no foreign_keys is set, even though the
- user is passing screwed up information, it is assumed
- that primary/secondaryjoin expressions should
- consider only and all cols in "secondary" to be
- foreign. It's not possible with "secondary" for
- the foreign keys to be elsewhere in any case.
- A warning is now emitted instead of an error,
- and the mapping succeeds.
-
- .. change::
- :tags: orm
- :tickets: 1856
-
- Moving an o2m object from one collection to
- another, or vice versa changing the referenced
- object by an m2o, where the foreign key is also a
- member of the primary key, will now be more
- carefully checked during flush if the change in
- value of the foreign key on the "many" side is the
- result of a change in the primary key of the "one"
- side, or if the "one" is just a different object.
- In one case, a cascade-capable DB would have
- cascaded the value already and we need to look at
- the "new" PK value to do an UPDATE, in the other we
- need to continue looking at the "old". We now look
- at the "old", assuming passive_updates=True,
- unless we know it was a PK switch that
- triggered the change.
-
- .. change::
- :tags: orm
- :tickets: 1857
-
- The value of version_id_col can be changed
- manually, and this will result in an UPDATE
- of the row. Versioned UPDATEs and DELETEs
- now use the "committed" value of the
- version_id_col in the WHERE clause and
- not the pending changed value. The
- version generator is also bypassed if
- manual changes are present on the attribute.
-
- .. change::
- :tags: orm
- :tickets:
-
- Repaired the usage of merge() when used with
- concrete inheriting mappers. Such mappers frequently
- have so-called "concrete" attributes, which are
- subclass attributes that "disable" propagation from
- the parent - these needed to allow a merge()
- operation to pass through without effect.
-
- .. change::
- :tags: orm
- :tickets: 1863
-
- Specifying a non-column based argument
- for column_mapped_collection, including string,
- text() etc., will raise an error message that
- specifically asks for a column element, no longer
- misleads with incorrect information about
- text() or literal().
-
- .. change::
- :tags: orm
- :tickets:
-
- Similarly, for relationship(), foreign_keys,
- remote_side, order_by - all column-based
- expressions are enforced - lists of strings
- are explicitly disallowed since this is a
- very common error
-
- .. change::
- :tags: orm
- :tickets: 1864
-
- Dynamic attributes don't support collection
- population - added an assertion for when
- set_committed_value() is called, as well as
- when joinedload() or subqueryload() options
- are applied to a dynamic attribute, instead
- of failure / silent failure.
-
- .. change::
- :tags: orm
- :tickets: 1852
-
- Fixed bug whereby generating a Query derived
- from one which had the same column repeated
- with different label names, typically
- in some UNION situations, would fail to
- propagate the inner columns completely to
- the outer query.
-
- .. change::
- :tags: orm
- :tickets: 1881
-
- object_session() raises the proper
- UnmappedInstanceError when presented with an
- unmapped instance.
-
- .. change::
- :tags: orm
- :tickets:
-
- Applied further memoizations to calculated Mapper
- properties, with significant (~90%) runtime mapper.py
- call count reduction in heavily polymorphic mapping
- configurations.
-
- .. change::
- :tags: orm
- :tickets:
-
- mapper _get_col_to_prop private method used
- by the versioning example is deprecated;
- now use mapper.get_property_by_column() which
- will remain the public method for this.
-
- .. change::
- :tags: orm
- :tickets:
-
- the versioning example works correctly now
- if versioning on a col that was formerly
- NULL.
-
- .. change::
- :tags: sql
- :tickets:
-
- Calling execute() on an alias() construct is pending
- deprecation for 0.7, as it is not itself an
- "executable" construct. It currently "proxies" its
- inner element and is conditionally "executable" but
- this is not the kind of ambiguity we like these days.
-
- .. change::
- :tags: sql
- :tickets:
-
- The execute() and scalar() methods of ClauseElement
- are now moved appropriately to the Executable
- subclass. ClauseElement.execute()/ scalar() are still
- present and are pending deprecation in 0.7, but note
- these would always raise an error anyway if you were
- not an Executable (unless you were an alias(), see
- previous note).
-
- .. change::
- :tags: sql
- :tickets:
-
- Added basic math expression coercion for
- Numeric->Integer,
- so that resulting type is Numeric regardless
- of the direction of the expression.
-
- .. change::
- :tags: sql
- :tickets: 1855
-
- Changed the scheme used to generate truncated
- "auto" index names when using the "index=True"
- flag on Column. The truncation only takes
- place with the auto-generated name, not one
- that is user-defined (an error would be
- raised instead), and the truncation scheme
- itself is now based on a fragment of an md5
- hash of the identifier name, so that multiple
- indexes on columns with similar names still
- have unique names.
-
- .. change::
- :tags: sql
- :tickets: 1412
-
- The generated index name also is based on
- a "max index name length" attribute which is
- separate from the "max identifier length" -
- this to appease MySQL who has a max length
- of 64 for index names, separate from their
- overall max length of 255.
-
- .. change::
- :tags: sql
- :tickets:
-
- the text() construct, if placed in a column
- oriented situation, will at least return NULLTYPE
- for its type instead of None, allowing it to
- be used a little more freely for ad-hoc column
- expressions than before. literal_column()
- is still the better choice, however.
-
- .. change::
- :tags: sql
- :tickets:
-
- Added full description of parent table/column,
- target table/column in error message raised when
- ForeignKey can't resolve target.
-
- .. change::
- :tags: sql
- :tickets: 1865
-
- Fixed bug whereby replacing composite foreign key
- columns in a reflected table would cause an attempt
- to remove the reflected constraint from the table
- a second time, raising a KeyError.
-
- .. change::
- :tags: sql
- :tickets:
-
- the _Label construct, i.e. the one that is produced
- whenever you say somecol.label(), now counts itself
- in its "proxy_set" unioned with that of its
- contained column's proxy set, instead of
- directly returning that of the contained column.
- This allows column correspondence
- operations which depend on the identity of the
- _Labels themselves to return the correct result
-
- .. change::
- :tags: sql
- :tickets: 1852
-
- fixes ORM bug.
-
- .. change::
- :tags: engine
- :tickets:
-
- Calling fetchone() or similar on a result that
- has already been exhausted, has been closed,
- or is not a result-returning result now
- raises ResourceClosedError, a subclass of
- InvalidRequestError, in all cases, regardless
- of backend. Previously, some DBAPIs would
- raise ProgrammingError (i.e. pysqlite), others
- would return None leading to downstream breakages
- (i.e. MySQL-python).
-
- .. change::
- :tags: engine
- :tickets: 1894
-
- Fixed bug in Connection whereby if a "disconnect"
- event occurred in the "initialize" phase of the
- first connection pool connect, an AttributeError
- would be raised when the Connection would attempt
- to invalidate the DBAPI connection.
-
- .. change::
- :tags: engine
- :tickets:
-
- Connection, ResultProxy, as well as Session use
- ResourceClosedError for all "this
- connection/transaction/result is closed" types of
- errors.
-
- .. change::
- :tags: engine
- :tickets:
-
- Connection.invalidate() can be called more than
- once and subsequent calls do nothing.
-
- .. change::
- :tags: declarative
- :tickets:
-
- if @classproperty is used with a regular class-bound
- mapper property attribute, it will be called to get the
- actual attribute value during initialization. Currently,
- there's no advantage to using @classproperty on a column
- or relationship attribute of a declarative class that
- isn't a mixin - evaluation is at the same time as if
- @classproperty weren't used. But here we at least allow
- it to function as expected.
-
- .. change::
- :tags: declarative
- :tickets:
-
- Fixed bug where "Can't add additional column" message
- would display the wrong name.
-
- .. change::
- :tags: postgresql
- :tickets:
-
- Fixed the psycopg2 dialect to use its
- set_isolation_level() method instead of relying
- upon the base "SET SESSION ISOLATION" command,
- as psycopg2 resets the isolation level on each new
- transaction otherwise.
-
- .. change::
- :tags: mssql
- :tickets:
-
- Fixed "default schema" query to work with
- pymssql backend.
-
- .. change::
- :tags: firebird
- :tickets:
-
- Fixed bug whereby a column default would fail to
- reflect if the "default" keyword were lower case.
-
- .. change::
- :tags: oracle
- :tickets: 1879
-
- Added ROWID type to the Oracle dialect, for those
- cases where an explicit CAST might be needed.
-
- .. change::
- :tags: oracle
- :tickets: 1867
-
- Oracle reflection of indexes has been tuned so
- that indexes which include some or all primary
- key columns, but not the same set of columns
- as that of the primary key, are reflected.
- Indexes which contain the identical columns
- as that of the primary key are skipped within
- reflection, as the index in that case is assumed
- to be the auto-generated primary key index.
- Previously, any index with PK columns present
- would be skipped. Thanks to Kent Bower
- for the patch.
-
- .. change::
- :tags: oracle
- :tickets: 1868
-
- Oracle now reflects the names of primary key
- constraints - also thanks to Kent Bower.
-
- .. change::
- :tags: informix
- :tickets: 1904
-
- Applied patches from to get
- basic Informix functionality up again. We
- rely upon end-user testing to ensure that
- Informix is working to some degree.
-
- .. change::
- :tags: documentation
- :tickets:
-
- The docs have been reorganized such that the "API
- Reference" section is gone - all the docstrings from
- there which were public API are moved into the
- context of the main doc section that talks about it.
- Main docs divided into "SQLAlchemy Core" and
- "SQLAlchemy ORM" sections, mapper/relationship docs
- have been broken out. Lots of sections rewritten
- and/or reorganized.
-
- .. change::
- :tags: examples
- :tickets:
-
- The beaker_caching example has been reorganized
- such that the Session, cache manager,
- declarative_base are part of environment, and
- custom cache code is portable and now within
- "caching_query.py". This allows the example to
- be easier to "drop in" to existing projects.
-
- .. change::
- :tags: examples
- :tickets: 1887
-
- the history_meta versioning recipe sets "unique=False"
- when copying columns, so that the versioning
- table handles multiple rows with repeating values.
-
-.. changelog::
- :version: 0.6.3
- :released: Thu Jul 15 2010
-
- .. change::
- :tags: orm
- :tickets: 1845
-
- Removed errant many-to-many load in unitofwork
- which triggered unnecessarily on expired/unloaded
- collections. This load now takes place only if
- passive_updates is False and the parent primary
- key has changed, or if passive_deletes is False
- and a delete of the parent has occurred.
-
- .. change::
- :tags: orm
- :tickets: 1853
-
- Column-entities (i.e. query(Foo.id)) copy their
- state more fully when queries are derived from
- themselves + a selectable (i.e. from_self(),
- union(), etc.), so that join() and such have the
- correct state to work from.
-
- .. change::
- :tags: orm
- :tickets: 1853
-
- Fixed bug where Query.join() would fail if
- querying a non-ORM column then joining without
- an on clause when a FROM clause is already
- present, now raises a checked exception the
- same way it does when the clause is not
- present.
-
- .. change::
- :tags: orm
- :tickets: 1142
-
- Improved the check for an "unmapped class",
- including the case where the superclass is mapped
- but the subclass is not. Any attempts to access
- cls._sa_class_manager.mapper now raise
- UnmappedClassError().
-
- .. change::
- :tags: orm
- :tickets:
-
- Added "column_descriptions" accessor to Query,
- returns a list of dictionaries containing
- naming/typing information about the entities
- the Query will return. Can be helpful for
- building GUIs on top of ORM queries.
-
- .. change::
- :tags: mysql
- :tickets: 1848
-
- The _extract_error_code() method now works
- correctly with each MySQL dialect (
- MySQL-python, OurSQL, MySQL-Connector-Python,
- PyODBC). Previously,
- the reconnect logic would fail for OperationalError
- conditions, however since MySQLdb and OurSQL
- have their own reconnect feature, there was no
- symptom for these drivers here unless one
- watched the logs.
-
- .. change::
- :tags: oracle
- :tickets: 1840
-
- More tweaks to cx_oracle Decimal handling.
- "Ambiguous" numerics with no decimal place
- are coerced to int at the connection handler
- level. The advantage here is that ints
- come back as ints without SQLA type
- objects being involved and without needless
- conversion to Decimal first.
-
- Unfortunately, some exotic subquery cases
- can even see different types between
- individual result rows, so the Numeric
- handler, when instructed to return Decimal,
- can't take full advantage of "native decimal"
- mode and must run isinstance() on every value
- to check if its Decimal already. Reopen of
-
-.. changelog::
- :version: 0.6.2
- :released: Tue Jul 06 2010
-
- .. change::
- :tags: orm
- :tickets:
-
- Query.join() will check for a call of the
- form query.join(target, clause_expression),
- i.e. missing the tuple, and raise an informative
- error message that this is the wrong calling form.
-
- .. change::
- :tags: orm
- :tickets: 1824
-
- Fixed bug regarding flushes on self-referential
- bi-directional many-to-many relationships, where
- two objects made to mutually reference each other
- in one flush would fail to insert a row for both
- sides. Regression from 0.5.
-
- .. change::
- :tags: orm
- :tickets:
-
- the post_update feature of relationship() has been
- reworked architecturally to integrate more closely
- with the new 0.6 unit of work. The motivation
- for the change is so that multiple "post update"
- calls, each affecting different foreign key
- columns of the same row, are executed in a single
- UPDATE statement, rather than one UPDATE
- statement per column per row. Multiple row
- updates are also batched into executemany()s as
- possible, while maintaining consistent row ordering.
-
- .. change::
- :tags: orm
- :tickets:
-
- Query.statement, Query.subquery(), etc. now transfer
- the values of bind parameters, i.e. those specified
- by query.params(), into the resulting SQL expression.
- Previously the values would not be transferred
- and bind parameters would come out as None.
-
- .. change::
- :tags: orm
- :tickets:
-
- Subquery-eager-loading now works with Query objects
- which include params(), as well as get() Queries.
-
- .. change::
- :tags: orm
- :tickets:
-
- Can now call make_transient() on an instance that
- is referenced by parent objects via many-to-one,
- without the parent's foreign key value getting
- temporarily set to None - this was a function
- of the "detect primary key switch" flush handler.
- It now ignores objects that are no longer
- in the "persistent" state, and the parent's
- foreign key identifier is left unaffected.
-
- .. change::
- :tags: orm
- :tickets:
-
- query.order_by() now accepts False, which cancels
- any existing order_by() state on the Query, allowing
- subsequent generative methods to be called which do
- not support ORDER BY. This is not the same as the
- already existing feature of passing None, which
- suppresses any existing order_by() settings, including
- those configured on the mapper. False will make it
- as though order_by() was never called, while
- None is an active setting.
-
- .. change::
- :tags: orm
- :tickets:
-
- An instance which is moved to "transient", has
- an incomplete or missing set of primary key
- attributes, and contains expired attributes, will
- raise an InvalidRequestError if an expired attribute
- is accessed, instead of getting a recursion overflow.
-
- .. change::
- :tags: orm
- :tickets:
-
- The make_transient() function is now in the generated
- documentation.
-
- .. change::
- :tags: orm
- :tickets:
-
- make_transient() removes all "loader" callables from
- the state being made transient, removing any
- "expired" state - all unloaded attributes reset back
- to undefined, None/empty on access.
-
- .. change::
- :tags: sql
- :tickets: 1822
-
- The warning emitted by the Unicode and String types
- with convert_unicode=True no longer embeds the actual
- value passed. This so that the Python warning
- registry does not continue to grow in size, the warning
- is emitted once as per the warning filter settings,
- and large string values don't pollute the output.
-
- .. change::
- :tags: sql
- :tickets:
-
- Fixed bug that would prevent overridden clause
- compilation from working for "annotated" expression
- elements, which are often generated by the ORM.
-
- .. change::
- :tags: sql
- :tickets: 1400
-
- The argument to "ESCAPE" of a LIKE operator or similar
- is passed through render_literal_value(), which may
- implement escaping of backslashes.
-
- .. change::
- :tags: sql
- :tickets:
-
- Fixed bug in Enum type which blew away native_enum
- flag when used with TypeDecorators or other adaption
- scenarios.
-
- .. change::
- :tags: sql
- :tickets:
-
- Inspector hits bind.connect() when invoked to ensure
- initialize has been called. the internal name ".conn"
- is changed to ".bind", since that's what it is.
-
- .. change::
- :tags: sql
- :tickets:
-
- Modified the internals of "column annotation" such that
- a custom Column subclass can safely override
- _constructor to return Column, for the purposes of
- making "configurational" column classes that aren't
- involved in proxying, etc.
-
- .. change::
- :tags: sql
- :tickets: 1829
-
- Column.copy() takes along the "unique" attribute
- among others, fixes regarding declarative
- mixins
-
- .. change::
- :tags: postgresql
- :tickets: 1400
-
- render_literal_value() is overridden which escapes
- backslashes, currently applies to the ESCAPE clause
- of LIKE and similar expressions.
- Ultimately this will have to detect the value of
- "standard_conforming_strings" for full behavior.
-
- .. change::
- :tags: postgresql
- :tickets: 1836
-
- Won't generate "CREATE TYPE" / "DROP TYPE" if
- using types.Enum on a PG version prior to 8.3 -
- the supports_native_enum flag is fully
- honored.
-
- .. change::
- :tags: mysql
- :tickets: 1826
-
- MySQL dialect doesn't emit CAST() for MySQL version
- detected < 4.0.2. This allows the unicode
- check on connect to proceed.
-
- .. change::
- :tags: mysql
- :tickets:
-
- MySQL dialect now detects NO_BACKSLASH_ESCAPES sql
- mode, in addition to ANSI_QUOTES.
-
- .. change::
- :tags: mysql
- :tickets: 1400
-
- render_literal_value() is overridden which escapes
- backslashes, currently applies to the ESCAPE clause
- of LIKE and similar expressions. This behavior
- is derived from detecting the value of
- NO_BACKSLASH_ESCAPES.
-
- .. change::
- :tags: oracle
- :tickets: 1819
-
- Fixed ora-8 compatibility flags such that they
- don't cache a stale value from before the first
- database connection actually occurs.
-
- .. change::
- :tags: oracle
- :tickets: 1840
-
- Oracle's "native decimal" metadata begins to return
- ambiguous typing information about numerics
- when columns are embedded in subqueries as well
- as when ROWNUM is consulted with subqueries, as we
- do for limit/offset. We've added these ambiguous
- conditions to the cx_oracle "convert to Decimal()"
- handler, so that we receive numerics as Decimal
- in more cases instead of as floats. These are
- then converted, if requested, into Integer
- or Float, or otherwise kept as the lossless
- Decimal.
-
- .. change::
- :tags: mssql
- :tickets: 1825
-
- If server_version_info is outside the usual
- range of (8, ), (9, ), (10, ), a warning is emitted
- which suggests checking that the FreeTDS version
- configuration is using 7.0 or 8.0, not 4.2.
-
- .. change::
- :tags: firebird
- :tickets: 1823
-
- Fixed incorrect signature in do_execute(), error
- introduced in 0.6.1.
-
- .. change::
- :tags: firebird
- :tickets: 1813
-
- Firebird dialect adds CHAR, VARCHAR types which
- accept a "charset" flag, to support Firebird
- "CHARACTER SET" clause.
-
- .. change::
- :tags: declarative
- :tickets: 1805, 1796, 1751
-
- Added support for @classproperty to provide
- any kind of schema/mapping construct from a
- declarative mixin, including columns with foreign
- keys, relationships, column_property, deferred.
- This solves all such issues on declarative mixins.
- An error is raised if any MapperProperty subclass
- is specified on a mixin without using @classproperty.
-
- .. change::
- :tags: declarative
- :tickets: 1821
-
- a mixin class can now define a column that matches
- one which is present on a __table__ defined on a
- subclass. It cannot, however, define one that is
- not present in the __table__, and the error message
- here now works.
-
- .. change::
- :tags: extension, compiler
- :tickets: 1838
-
- The 'default' compiler is automatically copied over
- when overriding the compilation of a built in
- clause construct, so no KeyError is raised if the
- user-defined compiler is specific to certain
- backends and compilation for a different backend
- is invoked.
-
- .. change::
- :tags: documentation
- :tickets: 1820
-
- Added documentation for the Inspector.
-
- .. change::
- :tags: documentation
- :tickets: 1830
-
- Fixed @memoized_property and @memoized_instancemethod
- decorators so that Sphinx documentation picks up
- these attributes and methods, such as
- ResultProxy.inserted_primary_key.
-
-.. changelog::
- :version: 0.6.1
- :released: Mon May 31 2010
-
- .. change::
- :tags: orm
- :tickets: 1782
-
- Fixed regression introduced in 0.6.0 involving improper
- history accounting on mutable attributes.
-
- .. change::
- :tags: orm
- :tickets: 1807
-
- Fixed regression introduced in 0.6.0 unit of work refactor
- that broke updates for bi-directional relationship()
- with post_update=True.
-
- .. change::
- :tags: orm
- :tickets: 1789
-
- session.merge() will not expire attributes on the returned
- instance if that instance is "pending".
-
- .. change::
- :tags: orm
- :tickets: 1802
-
- fixed __setstate__ method of CollectionAdapter to not
- fail during deserialize where parent InstanceState not
- yet unserialized.
-
- .. change::
- :tags: orm
- :tickets: 1797
-
- Added internal warning in case an instance without a
- full PK happened to be expired and then was asked
- to refresh.
-
- .. change::
- :tags: orm
- :tickets:
-
- Added more aggressive caching to the mapper's usage of
- UPDATE, INSERT, and DELETE expressions. Assuming the
- statement has no per-object SQL expressions attached,
- the expression objects are cached by the mapper after
- the first create, and their compiled form is stored
- persistently in a cache dictionary for the duration of
- the related Engine. The cache is an LRUCache for the
- rare case that a mapper receives an extremely
- high number of different column patterns as UPDATEs.
-
- .. change::
- :tags: sql
- :tickets: 1793
-
- expr.in_() now accepts a text() construct as the argument.
- Grouping parenthesis are added automatically, i.e. usage
- is like `col.in_(text("select id from table"))`.
-
- .. change::
- :tags: sql
- :tickets:
-
- Columns of _Binary type (i.e. LargeBinary, BLOB, etc.)
- will coerce a "basestring" on the right side into a
- _Binary as well so that required DBAPI processing
- takes place.
-
- .. change::
- :tags: sql
- :tickets: 1801
-
- Added table.add_is_dependent_on(othertable), allows manual
- placement of dependency rules between two Table objects
- for use within create_all(), drop_all(), sorted_tables.
-
- .. change::
- :tags: sql
- :tickets: 1778
-
- Fixed bug that prevented implicit RETURNING from functioning
- properly with composite primary key that contained zeroes.
-
- .. change::
- :tags: sql
- :tickets:
-
- Fixed errant space character when generating ADD CONSTRAINT
- for a named UNIQUE constraint.
-
- .. change::
- :tags: sql
- :tickets: 1571
-
- Fixed "table" argument on constructor of ForeignKeyConstraint
-
- .. change::
- :tags: sql
- :tickets: 1786
-
- Fixed bug in connection pool cursor wrapper whereby if a
- cursor threw an exception on close(), the logging of the
- message would fail.
-
- .. change::
- :tags: sql
- :tickets:
-
- the _make_proxy() method of ColumnClause and Column now use
- self.__class__ to determine the class of object to be returned
- instead of hardcoding to ColumnClause/Column, making it slightly
- easier to produce specific subclasses of these which work in
- alias/subquery situations.
-
- .. change::
- :tags: sql
- :tickets: 1798
-
- func.XXX() doesn't inadvertently resolve to non-Function
- classes (e.g. fixes func.text()).
-
- .. change::
- :tags: engines
- :tickets: 1781
-
- Fixed building the C extensions on Python 2.4.
-
- .. change::
- :tags: engines
- :tickets:
-
- Pool classes will reuse the same "pool_logging_name" setting
- after a dispose() occurs.
-
- .. change::
- :tags: engines
- :tickets:
-
- Engine gains an "execution_options" argument and
- update_execution_options() method, which will apply to
- all connections generated by this engine.
-
- .. change::
- :tags: mysql
- :tickets: 1794
-
- func.sysdate() emits "SYSDATE()", i.e. with the ending
- parenthesis, on MySQL.
-
- .. change::
- :tags: sqlite
- :tickets: 1812
-
- Fixed concatenation of constraints when "PRIMARY KEY"
- constraint gets moved to column level due to SQLite
- AUTOINCREMENT keyword being rendered.
-
- .. change::
- :tags: oracle
- :tickets: 1775
-
- Added a check for cx_oracle versions lower than version 5,
- in which case the incompatible "output type handler" won't
- be used. This will impact decimal accuracy and some
- unicode handling issues.
-
- .. change::
- :tags: oracle
- :tickets: 1790
-
- Fixed use_ansi=False mode, which was producing broken
- WHERE clauses in pretty much all cases.
-
- .. change::
- :tags: oracle
- :tickets: 1808
-
- Re-established support for Oracle 8 with cx_oracle,
- including that use_ansi is set to False automatically,
- NVARCHAR2 and NCLOB are not rendered for Unicode,
- "native unicode" check doesn't fail, cx_oracle
- "native unicode" mode is disabled, VARCHAR() is emitted
- with bytes count instead of char count.
-
- .. change::
- :tags: oracle
- :tickets: 1670
-
- oracle_xe 5 doesn't accept a Python unicode object in
- its connect string in normal Python 2.x mode - so we coerce
- to str() directly. non-ascii characters aren't supported
- in connect strings here since we don't know what encoding
- we could use.
-
- .. change::
- :tags: oracle
- :tickets: 1815
-
- FOR UPDATE is emitted in the syntactically correct position
- when limit/offset is used, i.e. the ROWNUM subquery.
- However, Oracle can't really handle FOR UPDATE with ORDER BY
- or with subqueries, so its still not very usable, but at
- least SQLA gets the SQL past the Oracle parser.
-
- .. change::
- :tags: firebird
- :tickets: 1521
-
- Added a label to the query used within has_table() and
- has_sequence() to work with older versions of Firebird
- that don't provide labels for result columns.
-
- .. change::
- :tags: firebird
- :tickets: 1779
-
- Added integer coercion to the "type_conv" attribute when
- passed via query string, so that it is properly interpreted
- by Kinterbasdb.
-
- .. change::
- :tags: firebird
- :tickets: 1646
-
- Added 'connection shutdown' to the list of exception strings
- which indicate a dropped connection.
-
- .. change::
- :tags: sqlsoup
- :tickets: 1783
-
- the SqlSoup constructor accepts a `base` argument which specifies
- the base class to use for mapped classes, the default being
- `object`.
-
-.. changelog::
- :version: 0.6.0
- :released: Sun Apr 18 2010
-
- .. change::
- :tags: orm
- :tickets: 1742, 1081
-
- Unit of work internals have been rewritten. Units of work
- with large numbers of objects interdependent objects
- can now be flushed without recursion overflows
- as there is no longer reliance upon recursive calls. The number of internal structures now stays
- constant for a particular session state, regardless of
- how many relationships are present on mappings. The flow
- of events now corresponds to a linear list of steps,
- generated by the mappers and relationships based on actual
- work to be done, filtered through a single topological sort
- for correct ordering. Flush actions are assembled using
- far fewer steps and less memory.
-
- .. change::
- :tags: orm
- :tickets:
-
- Along with the UOW rewrite, this also removes an issue
- introduced in 0.6beta3 regarding topological cycle detection
- for units of work with long dependency cycles. We now use
- an algorithm written by Guido (thanks Guido!).
-
- .. change::
- :tags: orm
- :tickets: 1764
-
- one-to-many relationships now maintain a list of positive
- parent-child associations within the flush, preventing
- previous parents marked as deleted from cascading a
- delete or NULL foreign key set on those child objects,
- despite the end-user not removing the child from the old
- association.
-
- .. change::
- :tags: orm
- :tickets: 1495
-
- A collection lazy load will switch off default
- eagerloading on the reverse many-to-one side, since
- that loading is by definition unnecessary.
-
- .. change::
- :tags: orm
- :tickets:
-
- Session.refresh() now does an equivalent expire()
- on the given instance first, so that the "refresh-expire"
- cascade is propagated. Previously, refresh() was
- not affected in any way by the presence of "refresh-expire"
- cascade. This is a change in behavior versus that
- of 0.6beta2, where the "lockmode" flag passed to refresh()
- would cause a version check to occur. Since the instance
- is first expired, refresh() always upgrades the object
- to the most recent version.
-
- .. change::
- :tags: orm
- :tickets: 1754
-
- The 'refresh-expire' cascade, when reaching a pending object,
- will expunge the object if the cascade also includes
- "delete-orphan", or will simply detach it otherwise.
-
- .. change::
- :tags: orm
- :tickets: 1756
-
- id(obj) is no longer used internally within topological.py,
- as the sorting functions now require hashable objects
- only.
-
- .. change::
- :tags: orm
- :tickets:
-
- The ORM will set the docstring of all generated descriptors
- to None by default. This can be overridden using 'doc'
- (or if using Sphinx, attribute docstrings work too).
-
- .. change::
- :tags: orm
- :tickets:
-
- Added kw argument 'doc' to all mapper property callables
- as well as Column(). Will assemble the string 'doc' as
- the '__doc__' attribute on the descriptor.
-
- .. change::
- :tags: orm
- :tickets: 1761
-
- Usage of version_id_col on a backend that supports
- cursor.rowcount for execute() but not executemany() now works
- when a delete is issued (already worked for saves, since those
- don't use executemany()). For a backend that doesn't support
- cursor.rowcount at all, a warning is emitted the same
- as with saves.
-
- .. change::
- :tags: orm
- :tickets:
-
- The ORM now short-term caches the "compiled" form of
- insert() and update() constructs when flushing lists of
- objects of all the same class, thereby avoiding redundant
- compilation per individual INSERT/UPDATE within an
- individual flush() call.
-
- .. change::
- :tags: orm
- :tickets:
-
- internal getattr(), setattr(), getcommitted() methods
- on ColumnProperty, CompositeProperty, RelationshipProperty
- have been underscored (i.e. are private), signature has
- changed.
-
- .. change::
- :tags: engines
- :tickets: 1757
-
- The C extension now also works with DBAPIs which use custom
- sequences as row (and not only tuples).
-
- .. change::
- :tags: sql
- :tickets: 1755
-
- Restored some bind-labeling logic from 0.5 which ensures
- that tables with column names that overlap another column
- of the form "<tablename>_<columnname>" won't produce
- errors if column._label is used as a bind name during
- an UPDATE. Test coverage which wasn't present in 0.5
- has been added.
-
- .. change::
- :tags: sql
- :tickets: 1729
-
- somejoin.select(fold_equivalents=True) is no longer
- deprecated, and will eventually be rolled into a more
- comprehensive version of the feature for.
-
- .. change::
- :tags: sql
- :tickets: 1759
-
- the Numeric type raises an *enormous* warning when expected
- to convert floats to Decimal from a DBAPI that returns floats.
- This includes SQLite, Sybase, MS-SQL.
-
- .. change::
- :tags: sql
- :tickets:
-
- Fixed an error in expression typing which caused an endless
- loop for expressions with two NULL types.
-
- .. change::
- :tags: sql
- :tickets:
-
- Fixed bug in execution_options() feature whereby the existing
- Transaction and other state information from the parent
- connection would not be propagated to the sub-connection.
-
- .. change::
- :tags: sql
- :tickets:
-
- Added new 'compiled_cache' execution option. A dictionary
- where Compiled objects will be cached when the Connection
- compiles a clause expression into a dialect- and parameter-
- specific Compiled object. It is the user's responsibility to
- manage the size of this dictionary, which will have keys
- corresponding to the dialect, clause element, the column
- names within the VALUES or SET clause of an INSERT or UPDATE,
- as well as the "batch" mode for an INSERT or UPDATE statement.
-
- .. change::
- :tags: sql
- :tickets: 1769
-
- Added get_pk_constraint() to reflection.Inspector, similar
- to get_primary_keys() except returns a dict that includes the
- name of the constraint, for supported backends (PG so far).
-
- .. change::
- :tags: sql
- :tickets: 1771
-
- Table.create() and Table.drop() no longer apply metadata-
- level create/drop events.
-
- .. change::
- :tags: ext
- :tickets:
-
- the compiler extension now allows @compiles decorators
- on base classes that extend to child classes, @compiles
- decorators on child classes that aren't broken by a
- @compiles decorator on the base class.
-
- .. change::
- :tags: ext
- :tickets:
-
- Declarative will raise an informative error message
- if a non-mapped class attribute is referenced in the
- string-based relationship() arguments.
-
- .. change::
- :tags: ext
- :tickets:
-
- Further reworked the "mixin" logic in declarative to
- additionally allow __mapper_args__ as a @classproperty
- on a mixin, such as to dynamically assign polymorphic_identity.
-
- .. change::
- :tags: postgresql
- :tickets: 1071
-
- PostgreSQL now reflects sequence names associated with
- SERIAL columns correctly, after the name of the sequence
- has been changed. Thanks to Kumar McMillan for the patch.
-
- .. change::
- :tags: postgresql
- :tickets:
-
- Repaired missing import in psycopg2._PGNumeric type when
- unknown numeric is received.
-
- .. change::
- :tags: postgresql
- :tickets:
-
- psycopg2/pg8000 dialects now aware of REAL[], FLOAT[],
- DOUBLE_PRECISION[], NUMERIC[] return types without
- raising an exception.
-
- .. change::
- :tags: postgresql
- :tickets: 1769
-
- PostgreSQL reflects the name of primary key constraints,
- if one exists.
-
- .. change::
- :tags: oracle
- :tickets:
-
- Now using cx_oracle output converters so that the
- DBAPI returns natively the kinds of values we prefer:
-
- .. change::
- :tags: oracle
- :tickets: 1759
-
- NUMBER values with positive precision + scale convert
- to cx_oracle.STRING and then to Decimal. This
- allows perfect precision for the Numeric type when
- using cx_oracle.
-
- .. change::
- :tags: oracle
- :tickets:
-
- STRING/FIXED_CHAR now convert to unicode natively.
- SQLAlchemy's String types then don't need to
- apply any kind of conversions.
-
- .. change::
- :tags: firebird
- :tickets:
-
- The functionality of result.rowcount can be disabled on a
- per-engine basis by setting 'enable_rowcount=False'
- on create_engine(). Normally, cursor.rowcount is called
- after any UPDATE or DELETE statement unconditionally,
- because the cursor is then closed and Firebird requires
- an open cursor in order to get a rowcount. This
- call is slightly expensive however so it can be disabled.
- To re-enable on a per-execution basis, the
- 'enable_rowcount=True' execution option may be used.
-
- .. change::
- :tags: examples
- :tickets:
-
- Updated attribute_shard.py example to use a more robust
- method of searching a Query for binary expressions which
- compare columns against literal values.
-
-.. changelog::
- :version: 0.6beta3
- :released: Sun Mar 28 2010
-
- .. change::
- :tags: orm
- :tickets: 1675
-
- Major feature: Added new "subquery" loading capability to
- relationship(). This is an eager loading option which
- generates a second SELECT for each collection represented
- in a query, across all parents at once. The query
- re-issues the original end-user query wrapped in a subquery,
- applies joins out to the target collection, and loads
- all those collections fully in one result, similar to
- "joined" eager loading but using all inner joins and not
- re-fetching full parent rows repeatedly (as most DBAPIs seem
- to do, even if columns are skipped). Subquery loading is
- available at mapper config level using "lazy='subquery'" and
- at the query options level using "subqueryload(props..)",
- "subqueryload_all(props...)".
-
- .. change::
- :tags: orm
- :tickets:
-
- To accommodate the fact that there are now two kinds of eager
- loading available, the new names for eagerload() and
- eagerload_all() are joinedload() and joinedload_all(). The
- old names will remain as synonyms for the foreseeable future.
-
- .. change::
- :tags: orm
- :tickets:
-
- The "lazy" flag on the relationship() function now accepts
- a string argument for all kinds of loading: "select", "joined",
- "subquery", "noload" and "dynamic", where the default is now
- "select". The old values of True/
- False/None still retain their usual meanings and will remain
- as synonyms for the foreseeable future.
-
- .. change::
- :tags: orm
- :tickets: 921
-
- Added with_hint() method to Query() construct. This calls
- directly down to select().with_hint() and also accepts
- entities as well as tables and aliases. See with_hint() in the
- SQL section below.
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed bug in Query whereby calling q.join(prop).from_self(...).
- join(prop) would fail to render the second join outside the
- subquery, when joining on the same criterion as was on the
- inside.
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed bug in Query whereby the usage of aliased() constructs
- would fail if the underlying table (but not the actual alias)
- were referenced inside the subquery generated by
- q.from_self() or q.select_from().
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed bug which affected all eagerload() and similar options
- such that "remote" eager loads, i.e. eagerloads off of a lazy
- load such as query(A).options(eagerload(A.b, B.c))
- wouldn't eagerload anything, but using eagerload("b.c") would
- work fine.
-
- .. change::
- :tags: orm
- :tickets:
-
- Query gains an add_columns(\*columns) method which is a multi-
- version of add_column(col). add_column(col) is future
- deprecated.
-
- .. change::
- :tags: orm
- :tickets:
-
- Query.join() will detect if the end result will be
- "FROM A JOIN A", and will raise an error if so.
-
- .. change::
- :tags: orm
- :tickets:
-
- Query.join(Cls.propname, from_joinpoint=True) will check more
- carefully that "Cls" is compatible with the current joinpoint,
- and act the same way as Query.join("propname", from_joinpoint=True)
- in that regard.
-
- .. change::
- :tags: sql
- :tickets: 921
-
- Added with_hint() method to select() construct. Specify
- a table/alias, hint text, and optional dialect name, and
- "hints" will be rendered in the appropriate place in the
- statement. Works for Oracle, Sybase, MySQL.
-
- .. change::
- :tags: sql
- :tickets: 1747
-
- Fixed bug introduced in 0.6beta2 where column labels would
- render inside of column expressions already assigned a label.
-
- .. change::
- :tags: postgresql
- :tickets: 877
-
- The psycopg2 dialect will log NOTICE messages via the
- "sqlalchemy.dialects.postgresql" logger name.
-
- .. change::
- :tags: postgresql
- :tickets: 997
-
- the TIME and TIMESTAMP types are now available from the
- postgresql dialect directly, which add the PG-specific
- argument 'precision' to both. 'precision' and
- 'timezone' are correctly reflected for both TIME and
- TIMEZONE types.
-
- .. change::
- :tags: mysql
- :tickets: 1752
-
- No longer guessing that TINYINT(1) should be BOOLEAN
- when reflecting - TINYINT(1) is returned. Use Boolean/
- BOOLEAN in table definition to get boolean conversion
- behavior.
-
- .. change::
- :tags: oracle
- :tickets: 1744
-
- The Oracle dialect will issue VARCHAR type definitions
- using character counts, i.e. VARCHAR2(50 CHAR), so that
- the column is sized in terms of characters and not bytes.
- Column reflection of character types will also use
- ALL_TAB_COLUMNS.CHAR_LENGTH instead of
- ALL_TAB_COLUMNS.DATA_LENGTH. Both of these behaviors take
- effect when the server version is 9 or higher - for
- version 8, the old behaviors are used.
-
- .. change::
- :tags: declarative
- :tickets: 1746
-
- Using a mixin won't break if the mixin implements an
- unpredictable __getattribute__(), i.e. Zope interfaces.
-
- .. change::
- :tags: declarative
- :tickets: 1749
-
- Using @classdecorator and similar on mixins to define
- __tablename__, __table_args__, etc. now works if
- the method references attributes on the ultimate
- subclass.
-
- .. change::
- :tags: declarative
- :tickets: 1751
-
- relationships and columns with foreign keys aren't
- allowed on declarative mixins, sorry.
-
- .. change::
- :tags: ext
- :tickets:
-
- The sqlalchemy.orm.shard module now becomes an extension,
- sqlalchemy.ext.horizontal_shard. The old import
- works with a deprecation warning.
-
-.. changelog::
- :version: 0.6beta2
- :released: Sat Mar 20 2010
-
- .. change::
- :tags: py3k
- :tickets:
-
- Improved the installation/test setup regarding Python 3,
- now that Distribute runs on Py3k. distribute_setup.py
- is now included. See README.py3k for Python 3 installation/
- testing instructions.
-
- .. change::
- :tags: orm
- :tickets: 1740
-
- The official name for the relation() function is now
- relationship(), to eliminate confusion over the relational
- algebra term. relation() however will remain available
- in equal capacity for the foreseeable future.
-
- .. change::
- :tags: orm
- :tickets: 1692
-
- Added "version_id_generator" argument to Mapper, this is a
- callable that, given the current value of the "version_id_col",
- returns the next version number. Can be used for alternate
- versioning schemes such as uuid, timestamps.
-
- .. change::
- :tags: orm
- :tickets:
-
- added "lockmode" kw argument to Session.refresh(), will
- pass through the string value to Query the same as
- in with_lockmode(), will also do version check for a
- version_id_col-enabled mapping.
-
- .. change::
- :tags: orm
- :tickets: 1188
-
- Fixed bug whereby calling query(A).join(A.bs).add_entity(B)
- in a joined inheritance scenario would double-add B as a
- target and produce an invalid query.
-
- .. change::
- :tags: orm
- :tickets: 1674
-
- Fixed bug in session.rollback() which involved not removing
- formerly "pending" objects from the session before
- re-integrating "deleted" objects, typically occurred with
- natural primary keys. If there was a primary key conflict
- between them, the attach of the deleted would fail
- internally. The formerly "pending" objects are now expunged
- first.
-
- .. change::
- :tags: orm
- :tickets: 1719
-
- Removed a lot of logging that nobody really cares about,
- logging that remains will respond to live changes in the
- log level. No significant overhead is added.
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed bug in session.merge() which prevented dict-like
- collections from merging.
-
- .. change::
- :tags: orm
- :tickets:
-
- session.merge() works with relations that specifically
- don't include "merge" in their cascade options - the target
- is ignored completely.
-
- .. change::
- :tags: orm
- :tickets: 1681
-
- session.merge() will not expire existing scalar attributes
- on an existing target if the target has a value for that
- attribute, even if the incoming merged doesn't have
- a value for the attribute. This prevents unnecessary loads
- on existing items. Will still mark the attr as expired
- if the destination doesn't have the attr, though, which
- fulfills some contracts of deferred cols.
-
- .. change::
- :tags: orm
- :tickets: 1680
-
- The "allow_null_pks" flag is now called "allow_partial_pks",
- defaults to True, acts like it did in 0.5 again. Except,
- it also is implemented within merge() such that a SELECT
- won't be issued for an incoming instance with partially
- NULL primary key if the flag is False.
-
- .. change::
- :tags: orm
- :tickets: 1737
-
- Fixed bug in 0.6-reworked "many-to-one" optimizations
- such that a many-to-one that is against a non-primary key
- column on the remote table (i.e. foreign key against a
- UNIQUE column) will pull the "old" value in from the
- database during a change, since if it's in the session
- we will need it for proper history/backref accounting,
- and we can't pull from the local identity map on a
- non-primary key column.
-
- .. change::
- :tags: orm
- :tickets: 1731
-
- fixed internal error which would occur if calling has()
- or similar complex expression on a single-table inheritance
- relation().
-
- .. change::
- :tags: orm
- :tickets: 1688
-
- query.one() no longer applies LIMIT to the query, this to
- ensure that it fully counts all object identities present
- in the result, even in the case where joins may conceal
- multiple identities for two or more rows. As a bonus,
- one() can now also be called with a query that issued
- from_statement() to start with since it no longer modifies
- the query.
-
- .. change::
- :tags: orm
- :tickets: 1727
-
- query.get() now returns None if queried for an identifier
- that is present in the identity map with a different class
- than the one requested, i.e. when using polymorphic loading.
-
- .. change::
- :tags: orm
- :tickets: 1706
-
- A major fix in query.join(), when the "on" clause is an
- attribute of an aliased() construct, but there is already
- an existing join made out to a compatible target, query properly
- joins to the right aliased() construct instead of sticking
- onto the right side of the existing join.
-
- .. change::
- :tags: orm
- :tickets: 1362
-
- Slight improvement to the fix for to not issue
- needless updates of the primary key column during a so-called
- "row switch" operation, i.e. add + delete of two objects
- with the same PK.
-
- .. change::
- :tags: orm
- :tickets:
-
- Now uses sqlalchemy.orm.exc.DetachedInstanceError when an
- attribute load or refresh action fails due to object
- being detached from any Session. UnboundExecutionError
- is specific to engines bound to sessions and statements.
-
- .. change::
- :tags: orm
- :tickets:
-
- Query called in the context of an expression will render
- disambiguating labels in all cases. Note that this does
- not apply to the existing .statement and .subquery()
- accessor/method, which still honors the .with_labels()
- setting that defaults to False.
-
- .. change::
- :tags: orm
- :tickets: 1676
-
- Query.union() retains disambiguating labels within the
- returned statement, thus avoiding various SQL composition
- errors which can result from column name conflicts.
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed bug in attribute history that inadvertently invoked
- __eq__ on mapped instances.
-
- .. change::
- :tags: orm
- :tickets:
-
- Some internal streamlining of object loading grants a
- small speedup for large results, estimates are around
- 10-15%. Gave the "state" internals a good solid
- cleanup with less complexity, datamembers,
- method calls, blank dictionary creates.
-
- .. change::
- :tags: orm
- :tickets: 1689
-
- Documentation clarification for query.delete()
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed cascade bug in many-to-one relation() when attribute
- was set to None, introduced in r6711 (cascade deleted
- items into session during add()).
-
- .. change::
- :tags: orm
- :tickets: 1736
-
- Calling query.order_by() or query.distinct() before calling
- query.select_from(), query.with_polymorphic(), or
- query.from_statement() raises an exception now instead of
- silently dropping those criterion.
-
- .. change::
- :tags: orm
- :tickets: 1735
-
- query.scalar() now raises an exception if more than one
- row is returned. All other behavior remains the same.
-
- .. change::
- :tags: orm
- :tickets: 1692
-
- Fixed bug which caused "row switch" logic, that is an
- INSERT and DELETE replaced by an UPDATE, to fail when
- version_id_col was in use.
-
- .. change::
- :tags: sql
- :tickets: 1714
-
- join() will now simulate a NATURAL JOIN by default. Meaning,
- if the left side is a join, it will attempt to join the right
- side to the rightmost side of the left first, and not raise
- any exceptions about ambiguous join conditions if successful
- even if there are further join targets across the rest of
- the left.
-
- .. change::
- :tags: sql
- :tickets:
-
- The most common result processors conversion function were
- moved to the new "processors" module. Dialect authors are
- encouraged to use those functions whenever they correspond
- to their needs instead of implementing custom ones.
-
- .. change::
- :tags: sql
- :tickets: 1694, 1698
-
- SchemaType and subclasses Boolean, Enum are now serializable,
- including their ddl listener and other event callables.
-
- .. change::
- :tags: sql
- :tickets:
-
- Some platforms will now interpret certain literal values
- as non-bind parameters, rendered literally into the SQL
- statement. This to support strict SQL-92 rules that are
- enforced by some platforms including MS-SQL and Sybase.
- In this model, bind parameters aren't allowed in the
- columns clause of a SELECT, nor are certain ambiguous
- expressions like "?=?". When this mode is enabled, the base
- compiler will render the binds as inline literals, but only across
- strings and numeric values. Other types such as dates
- will raise an error, unless the dialect subclass defines
- a literal rendering function for those. The bind parameter
- must have an embedded literal value already or an error
- is raised (i.e. won't work with straight bindparam('x')).
- Dialects can also expand upon the areas where binds are not
- accepted, such as within argument lists of functions
- (which don't work on MS-SQL when native SQL binding is used).
-
- .. change::
- :tags: sql
- :tickets:
-
- Added "unicode_errors" parameter to String, Unicode, etc.
- Behaves like the 'errors' keyword argument to
- the standard library's string.decode() functions. This flag
- requires that `convert_unicode` is set to `"force"` - otherwise,
- SQLAlchemy is not guaranteed to handle the task of unicode
- conversion. Note that this flag adds significant performance
- overhead to row-fetching operations for backends that already
- return unicode objects natively (which most DBAPIs do). This
- flag should only be used as an absolute last resort for reading
- strings from a column with varied or corrupted encodings,
- which only applies to databases that accept invalid encodings
- in the first place (i.e. MySQL. *not* PG, Sqlite, etc.)
-
- .. change::
- :tags: sql
- :tickets:
-
- Added math negation operator support, -x.
-
- .. change::
- :tags: sql
- :tickets:
-
- FunctionElement subclasses are now directly executable the
- same way any func.foo() construct is, with automatic
- SELECT being applied when passed to execute().
-
- .. change::
- :tags: sql
- :tickets:
-
- The "type" and "bind" keyword arguments of a func.foo()
- construct are now local to "func." constructs and are
- not part of the FunctionElement base class, allowing
- a "type" to be handled in a custom constructor or
- class-level variable.
-
- .. change::
- :tags: sql
- :tickets:
-
- Restored the keys() method to ResultProxy.
-
- .. change::
- :tags: sql
- :tickets: 1647, 1683
-
- The type/expression system now does a more complete job
- of determining the return type from an expression
- as well as the adaptation of the Python operator into
- a SQL operator, based on the full left/right/operator
- of the given expression. In particular
- the date/time/interval system created for PostgreSQL
- EXTRACT in has now been generalized into
- the type system. The previous behavior which often
- occurred of an expression "column + literal" forcing
- the type of "literal" to be the same as that of "column"
- will now usually not occur - the type of
- "literal" is first derived from the Python type of the
- literal, assuming standard native Python types + date
- types, before falling back to that of the known type
- on the other side of the expression. If the
- "fallback" type is compatible (i.e. CHAR from String),
- the literal side will use that. TypeDecorator
- types override this by default to coerce the "literal"
- side unconditionally, which can be changed by implementing
- the coerce_compared_value() method. Also part of.
-
- .. change::
- :tags: sql
- :tickets:
-
- Made sqlalchemy.sql.expressions.Executable part of public
- API, used for any expression construct that can be sent to
- execute(). FunctionElement now inherits Executable so that
- it gains execution_options(), which are also propagated
- to the select() that's generated within execute().
- Executable in turn subclasses _Generative which marks
- any ClauseElement that supports the @_generative
- decorator - these may also become "public" for the benefit
- of the compiler extension at some point.
-
- .. change::
- :tags: sql
- :tickets: 1579
-
- A change to the solution for - an end-user
- defined bind parameter name that directly conflicts with
- a column-named bind generated directly from the SET or
- VALUES clause of an update/insert generates a compile error.
- This reduces call counts and eliminates some cases where
- undesirable name conflicts could still occur.
-
- .. change::
- :tags: sql
- :tickets: 1705
-
- Column() requires a type if it has no foreign keys (this is
- not new). An error is now raised if a Column() has no type
- and no foreign keys.
-
- .. change::
- :tags: sql
- :tickets: 1717
-
- the "scale" argument of the Numeric() type is honored when
- coercing a returned floating point value into a string
- on its way to Decimal - this allows accuracy to function
- on SQLite, MySQL.
-
- .. change::
- :tags: sql
- :tickets:
-
- the copy() method of Column now copies over uninitialized
- "on table attach" events. Helps with the new declarative
- "mixin" capability.
-
- .. change::
- :tags: engines
- :tickets:
-
- Added an optional C extension to speed up the sql layer by
- reimplementing RowProxy and the most common result processors.
- The actual speedups will depend heavily on your DBAPI and
- the mix of datatypes used in your tables, and can vary from
- a 30% improvement to more than 200%. It also provides a modest
- (~15-20%) indirect improvement to ORM speed for large queries.
- Note that it is *not* built/installed by default.
- See README for installation instructions.
-
- .. change::
- :tags: engines
- :tickets:
-
- the execution sequence pulls all rowcount/last inserted ID
- info from the cursor before commit() is called on the
- DBAPI connection in an "autocommit" scenario. This helps
- mxodbc with rowcount and is probably a good idea overall.
-
- .. change::
- :tags: engines
- :tickets: 1719
-
- Opened up logging a bit such that isEnabledFor() is called
- more often, so that changes to the log level for engine/pool
- will be reflected on next connect. This adds a small
- amount of method call overhead. It's negligible and will make
- life a lot easier for all those situations when logging
- just happens to be configured after create_engine() is called.
-
- .. change::
- :tags: engines
- :tickets:
-
- The assert_unicode flag is deprecated. SQLAlchemy will raise
- a warning in all cases where it is asked to encode a non-unicode
- Python string, as well as when a Unicode or UnicodeType type
- is explicitly passed a bytestring. The String type will do nothing
- for DBAPIs that already accept Python unicode objects.
-
- .. change::
- :tags: engines
- :tickets:
-
- Bind parameters are sent as a tuple instead of a list. Some
- backend drivers will not accept bind parameters as a list.
-
- .. change::
- :tags: engines
- :tickets:
-
- threadlocal engine wasn't properly closing the connection
- upon close() - fixed that.
-
- .. change::
- :tags: engines
- :tickets:
-
- Transaction object doesn't rollback or commit if it isn't
- "active", allows more accurate nesting of begin/rollback/commit.
-
- .. change::
- :tags: engines
- :tickets:
-
- Python unicode objects as binds result in the Unicode type,
- not string, thus eliminating a certain class of unicode errors
- on drivers that don't support unicode binds.
-
- .. change::
- :tags: engines
- :tickets: 1555
-
- Added "logging_name" argument to create_engine(), Pool() constructor
- as well as "pool_logging_name" argument to create_engine() which
- filters down to that of Pool. Issues the given string name
- within the "name" field of logging messages instead of the default
- hex identifier string.
-
- .. change::
- :tags: engines
- :tickets:
-
- The visit_pool() method of Dialect is removed, and replaced with
- on_connect(). This method returns a callable which receives
- the raw DBAPI connection after each one is created. The callable
- is assembled into a first_connect/connect pool listener by the
- connection strategy if non-None. Provides a simpler interface
- for dialects.
-
- .. change::
- :tags: engines
- :tickets: 1728
-
- StaticPool now initializes, disposes and recreates without
- opening a new connection - the connection is only opened when
- first requested. dispose() also works on AssertionPool now.
-
- .. change::
- :tags: ticket: 1673, metadata
- :tickets:
-
- Added the ability to strip schema information when using
- "tometadata" by passing "schema=None" as an argument. If schema
- is not specified then the table's schema is retained.
-
- .. change::
- :tags: declarative
- :tickets:
-
- DeclarativeMeta exclusively uses cls.__dict__ (not dict\_)
- as the source of class information; _as_declarative exclusively
- uses the dict\_ passed to it as the source of class information
- (which when using DeclarativeMeta is cls.__dict__). This should
- in theory make it easier for custom metaclasses to modify
- the state passed into _as_declarative.
-
- .. change::
- :tags: declarative
- :tickets: 1707
-
- declarative now accepts mixin classes directly, as a means
- to provide common functional and column-based elements on
- all subclasses, as well as a means to propagate a fixed
- set of __table_args__ or __mapper_args__ to subclasses.
- For custom combinations of __table_args__/__mapper_args__ from
- an inherited mixin to local, descriptors can now be used.
- New details are all up in the Declarative documentation.
- Thanks to Chris Withers for putting up with my strife
- on this.
-
- .. change::
- :tags: declarative
- :tickets: 1393
-
- the __mapper_args__ dict is copied when propagating to a subclass,
- and is taken straight off the class __dict__ to avoid any
- propagation from the parent. mapper inheritance already
- propagates the things you want from the parent mapper.
-
- .. change::
- :tags: declarative
- :tickets: 1732
-
- An exception is raised when a single-table subclass specifies
- a column that is already present on the base class.
-
- .. change::
- :tags: mysql
- :tickets: 1655
-
- Fixed reflection bug whereby when COLLATE was present,
- nullable flag and server defaults would not be reflected.
-
- .. change::
- :tags: mysql
- :tickets:
-
- Fixed reflection of TINYINT(1) "boolean" columns defined with
- integer flags like UNSIGNED.
-
- .. change::
- :tags: mysql
- :tickets: 1668
-
- Further fixes for the mysql-connector dialect.
-
- .. change::
- :tags: mysql
- :tickets: 1496
-
- Composite PK table on InnoDB where the "autoincrement" column
- isn't first will emit an explicit "KEY" phrase within
- CREATE TABLE thereby avoiding errors.
-
- .. change::
- :tags: mysql
- :tickets: 1634
-
- Added reflection/create table support for a wide range
- of MySQL keywords.
-
- .. change::
- :tags: mysql
- :tickets: 1580
-
- Fixed import error which could occur reflecting tables on
- a Windows host
-
- .. change::
- :tags: mssql
- :tickets:
-
- Re-established support for the pymssql dialect.
-
- .. change::
- :tags: mssql
- :tickets:
-
- Various fixes for implicit returning, reflection,
- etc. - the MS-SQL dialects aren't quite complete
- in 0.6 yet (but are close)
-
- .. change::
- :tags: mssql
- :tickets: 1710
-
- Added basic support for mxODBC.
-
- .. change::
- :tags: mssql
- :tickets:
-
- Removed the text_as_varchar option.
-
- .. change::
- :tags: oracle
- :tickets:
-
- "out" parameters require a type that is supported by
- cx_oracle. An error will be raised if no cx_oracle
- type can be found.
-
- .. change::
- :tags: oracle
- :tickets:
-
- Oracle 'DATE' now does not perform any result processing,
- as the DATE type in Oracle stores full date+time objects,
- that's what you'll get. Note that the generic types.Date
- type *will* still call value.date() on incoming values,
- however. When reflecting a table, the reflected type
- will be 'DATE'.
-
- .. change::
- :tags: oracle
- :tickets: 1670
-
- Added preliminary support for Oracle's WITH_UNICODE
- mode. At the very least this establishes initial
- support for cx_Oracle with Python 3. When WITH_UNICODE
- mode is used in Python 2.xx, a large and scary warning
- is emitted asking that the user seriously consider
- the usage of this difficult mode of operation.
-
- .. change::
- :tags: oracle
- :tickets: 1712
-
- The except_() method now renders as MINUS on Oracle,
- which is more or less equivalent on that platform.
-
- .. change::
- :tags: oracle
- :tickets: 651
-
- Added support for rendering and reflecting
- TIMESTAMP WITH TIME ZONE, i.e. TIMESTAMP(timezone=True).
-
- .. change::
- :tags: oracle
- :tickets:
-
- Oracle INTERVAL type can now be reflected.
-
- .. change::
- :tags: sqlite
- :tickets: 1685
-
- Added "native_datetime=True" flag to create_engine().
- This will cause the DATE and TIMESTAMP types to skip
- all bind parameter and result row processing, under
- the assumption that PARSE_DECLTYPES has been enabled
- on the connection. Note that this is not entirely
- compatible with the "func.current_date()", which
- will be returned as a string.
-
- .. change::
- :tags: sybase
- :tickets:
-
- Implemented a preliminary working dialect for Sybase,
- with sub-implementations for Python-Sybase as well
- as Pyodbc. Handles table
- creates/drops and basic round trip functionality.
- Does not yet include reflection or comprehensive
- support of unicode/special expressions/etc.
-
- .. change::
- :tags: examples
- :tickets:
-
- Changed the beaker cache example a bit to have a separate
- RelationCache option for lazyload caching. This object
- does a lookup among any number of potential attributes
- more efficiently by grouping several into a common structure.
- Both FromCache and RelationCache are simpler individually.
-
- .. change::
- :tags: documentation
- :tickets: 1700
-
- Major cleanup work in the docs to link class, function, and
- method names into the API docs.
-
-.. changelog::
- :version: 0.6beta1
- :released: Wed Feb 03 2010
-
- .. change::
- :tags: release, major
- :tickets:
-
- For the full set of feature descriptions, see
- https://docs.sqlalchemy.org/en/latest/changelog/migration_06.html .
- This document is a work in progress.
-
- .. change::
- :tags: release, major
- :tickets:
-
- All bug fixes and feature enhancements from the most
- recent 0.5 version and below are also included within 0.6.
-
- .. change::
- :tags: release, major
- :tickets:
-
- Platforms targeted now include Python 2.4/2.5/2.6, Python
- 3.1, Jython2.5.
-
- .. change::
- :tags: orm
- :tickets:
-
- Changes to query.update() and query.delete():
- - the 'expire' option on query.update() has been renamed to
- 'fetch', thus matching that of query.delete().
- 'expire' is deprecated and issues a warning.
-
- - query.update() and query.delete() both default to
- 'evaluate' for the synchronize strategy.
-
- - the 'synchronize' strategy for update() and delete()
- raises an error on failure. There is no implicit fallback
- onto "fetch". Failure of evaluation is based on the
- structure of criteria, so success/failure is deterministic
- based on code structure.
-
- .. change::
- :tags: orm
- :tickets: 1186, 1492, 1544
-
- Enhancements on many-to-one relations:
- - many-to-one relations now fire off a lazyload in fewer
- cases, including in most cases will not fetch the "old"
- value when a new one is replaced.
-
- - many-to-one relation to a joined-table subclass now uses
- get() for a simple load (known as the "use_get"
- condition), i.e. Related->Sub(Base), without the need to
- redefine the primaryjoin condition in terms of the base
- table.
-
- - specifying a foreign key with a declarative column, i.e.
- ForeignKey(MyRelatedClass.id) doesn't break the "use_get"
- condition from taking place
-
- - relation(), eagerload(), and eagerload_all() now feature
- an option called "innerjoin". Specify `True` or `False` to
- control whether an eager join is constructed as an INNER
- or OUTER join. Default is `False` as always. The mapper
- options will override whichever setting is specified on
- relation(). Should generally be set for many-to-one, not
- nullable foreign key relations to allow improved join
- performance.
-
- - the behavior of eagerloading such that the main query is
- wrapped in a subquery when LIMIT/OFFSET are present now
- makes an exception for the case when all eager loads are
- many-to-one joins. In those cases, the eager joins are
- against the parent table directly along with the
- limit/offset without the extra overhead of a subquery,
- since a many-to-one join does not add rows to the result.
-
- .. change::
- :tags: orm
- :tickets:
-
- Enhancements / Changes on Session.merge():
-
- .. change::
- :tags: orm
- :tickets:
-
- the "dont_load=True" flag on Session.merge() is deprecated
- and is now "load=False".
-
- .. change::
- :tags: orm
- :tickets:
-
- Session.merge() is performance optimized, using half the
- call counts for "load=False" mode compared to 0.5 and
- significantly fewer SQL queries in the case of collections
- for "load=True" mode.
-
- .. change::
- :tags: orm
- :tickets:
-
- merge() will not issue a needless merge of attributes if the
- given instance is the same instance which is already present.
-
- .. change::
- :tags: orm
- :tickets:
-
- merge() now also merges the "options" associated with a given
- state, i.e. those passed through query.options() which follow
- along with an instance, such as options to eagerly- or
- lazyily- load various attributes. This is essential for
- the construction of highly integrated caching schemes. This
- is a subtle behavioral change vs. 0.5.
-
- .. change::
- :tags: orm
- :tickets:
-
- A bug was fixed regarding the serialization of the "loader
- path" present on an instance's state, which is also necessary
- when combining the usage of merge() with serialized state
- and associated options that should be preserved.
-
- .. change::
- :tags: orm
- :tickets:
-
- The all new merge() is showcased in a new comprehensive
- example of how to integrate Beaker with SQLAlchemy. See
- the notes in the "examples" note below.
-
- .. change::
- :tags: orm
- :tickets: 1362
-
- Primary key values can now be changed on a joined-table inheritance
- object, and ON UPDATE CASCADE will be taken into account when
- the flush happens. Set the new "passive_updates" flag to False
- on mapper() when using SQLite or MySQL/MyISAM.
-
- .. change::
- :tags: orm
- :tickets: 1671
-
- flush() now detects when a primary key column was updated by
- an ON UPDATE CASCADE operation from another primary key, and
- can then locate the row for a subsequent UPDATE on the new PK
- value. This occurs when a relation() is there to establish
- the relationship as well as passive_updates=True.
-
- .. change::
- :tags: orm
- :tickets:
-
- the "save-update" cascade will now cascade the pending *removed*
- values from a scalar or collection attribute into the new session
- during an add() operation. This so that the flush() operation
- will also delete or modify rows of those disconnected items.
-
- .. change::
- :tags: orm
- :tickets: 1531
-
- Using a "dynamic" loader with a "secondary" table now produces
- a query where the "secondary" table is *not* aliased. This
- allows the secondary Table object to be used in the "order_by"
- attribute of the relation(), and also allows it to be used
- in filter criterion against the dynamic relation.
-
- .. change::
- :tags: orm
- :tickets: 1643
-
- relation() with uselist=False will emit a warning when
- an eager or lazy load locates more than one valid value for
- the row. This may be due to primaryjoin/secondaryjoin
- conditions which aren't appropriate for an eager LEFT OUTER
- JOIN or for other conditions.
-
- .. change::
- :tags: orm
- :tickets: 1633
-
- an explicit check occurs when a synonym() is used with
- map_column=True, when a ColumnProperty (deferred or otherwise)
- exists separately in the properties dictionary sent to mapper
- with the same keyname. Instead of silently replacing
- the existing property (and possible options on that property),
- an error is raised.
-
- .. change::
- :tags: orm
- :tickets:
-
- a "dynamic" loader sets up its query criterion at construction
- time so that the actual query is returned from non-cloning
- accessors like "statement".
-
- .. change::
- :tags: orm
- :tickets:
-
- the "named tuple" objects returned when iterating a
- Query() are now pickleable.
-
- .. change::
- :tags: orm
- :tickets: 1542
-
- mapping to a select() construct now requires that you
- make an alias() out of it distinctly. This to eliminate
- confusion over such issues as
-
- .. change::
- :tags: orm
- :tickets: 1537
-
- query.join() has been reworked to provide more consistent
- behavior and more flexibility (includes)
-
- .. change::
- :tags: orm
- :tickets:
-
- query.select_from() accepts multiple clauses to produce
- multiple comma separated entries within the FROM clause.
- Useful when selecting from multiple-homed join() clauses.
-
- .. change::
- :tags: orm
- :tickets:
-
- query.select_from() also accepts mapped classes, aliased()
- constructs, and mappers as arguments. In particular this
- helps when querying from multiple joined-table classes to ensure
- the full join gets rendered.
-
- .. change::
- :tags: orm
- :tickets: 1135
-
- query.get() can be used with a mapping to an outer join
- where one or more of the primary key values are None.
-
- .. change::
- :tags: orm
- :tickets: 1568
-
- query.from_self(), query.union(), others which do a
- "SELECT * from (SELECT...)" type of nesting will do
- a better job translating column expressions within the subquery
- to the columns clause of the outer query. This is
- potentially backwards incompatible with 0.5, in that this
- may break queries with literal expressions that do not have labels
- applied (i.e. literal('foo'), etc.)
-
- .. change::
- :tags: orm
- :tickets: 1622
-
- relation primaryjoin and secondaryjoin now check that they
- are column-expressions, not just clause elements. this prohibits
- things like FROM expressions being placed there directly.
-
- .. change::
- :tags: orm
- :tickets: 1415
-
- `expression.null()` is fully understood the same way
- None is when comparing an object/collection-referencing
- attribute within query.filter(), filter_by(), etc.
-
- .. change::
- :tags: orm
- :tickets: 1052
-
- added "make_transient()" helper function which transforms a
- persistent/ detached instance into a transient one (i.e.
- deletes the instance_key and removes from any session.)
-
- .. change::
- :tags: orm
- :tickets: 1339
-
- the allow_null_pks flag on mapper() is deprecated, and
- the feature is turned "on" by default. This means that
- a row which has a non-null value for any of its primary key
- columns will be considered an identity. The need for this
- scenario typically only occurs when mapping to an outer join.
-
- .. change::
- :tags: orm
- :tickets:
-
- the mechanics of "backref" have been fully merged into the
- finer grained "back_populates" system, and take place entirely
- within the _generate_backref() method of RelationProperty. This
- makes the initialization procedure of RelationProperty
- simpler and allows easier propagation of settings (such as from
- subclasses of RelationProperty) into the reverse reference.
- The internal BackRef() is gone and backref() returns a plain
- tuple that is understood by RelationProperty.
-
- .. change::
- :tags: orm
- :tickets: 1569
-
- The version_id_col feature on mapper() will raise a warning when
- used with dialects that don't support "rowcount" adequately.
-
- .. change::
- :tags: orm
- :tickets:
-
- added "execution_options()" to Query, to so options can be
- passed to the resulting statement. Currently only
- Select-statements have these options, and the only option
- used is "stream_results", and the only dialect which knows
- "stream_results" is psycopg2.
-
- .. change::
- :tags: orm
- :tickets:
-
- Query.yield_per() will set the "stream_results" statement
- option automatically.
-
- .. change::
- :tags: orm
- :tickets:
-
- Deprecated or removed:
- * 'allow_null_pks' flag on mapper() is deprecated. It does
- nothing now and the setting is "on" in all cases.
- * 'transactional' flag on sessionmaker() and others is
- removed. Use 'autocommit=True' to indicate 'transactional=False'.
- * 'polymorphic_fetch' argument on mapper() is removed.
- Loading can be controlled using the 'with_polymorphic'
- option.
- * 'select_table' argument on mapper() is removed. Use
- 'with_polymorphic=("*", <some selectable>)' for this
- functionality.
- * 'proxy' argument on synonym() is removed. This flag
- did nothing throughout 0.5, as the "proxy generation"
- behavior is now automatic.
- * Passing a single list of elements to eagerload(),
- eagerload_all(), contains_eager(), lazyload(),
- defer(), and undefer() instead of multiple positional
- \*args is deprecated.
- * Passing a single list of elements to query.order_by(),
- query.group_by(), query.join(), or query.outerjoin()
- instead of multiple positional \*args is deprecated.
- * query.iterate_instances() is removed. Use query.instances().
- * Query.query_from_parent() is removed. Use the
- sqlalchemy.orm.with_parent() function to produce a
- "parent" clause, or alternatively query.with_parent().
- * query._from_self() is removed, use query.from_self()
- instead.
- * the "comparator" argument to composite() is removed.
- Use "comparator_factory".
- * RelationProperty._get_join() is removed.
- * the 'echo_uow' flag on Session is removed. Use
- logging on the "sqlalchemy.orm.unitofwork" name.
- * session.clear() is removed. use session.expunge_all().
- * session.save(), session.update(), session.save_or_update()
- are removed. Use session.add() and session.add_all().
- * the "objects" flag on session.flush() remains deprecated.
- * the "dont_load=True" flag on session.merge() is deprecated
- in favor of "load=False".
- * ScopedSession.mapper remains deprecated. See the
- usage recipe at
- https://www.sqlalchemy.org/trac/wiki/UsageRecipes/SessionAwareMapper
- * passing an InstanceState (internal SQLAlchemy state object) to
- attributes.init_collection() or attributes.get_history() is
- deprecated. These functions are public API and normally
- expect a regular mapped object instance.
- * the 'engine' parameter to declarative_base() is removed.
- Use the 'bind' keyword argument.
-
- .. change::
- :tags: sql
- :tickets:
-
- the "autocommit" flag on select() and text() as well
- as select().autocommit() are deprecated - now call
- .execution_options(autocommit=True) on either of those
- constructs, also available directly on Connection and orm.Query.
-
- .. change::
- :tags: sql
- :tickets:
-
- the autoincrement flag on column now indicates the column
- which should be linked to cursor.lastrowid, if that method
- is used. See the API docs for details.
-
- .. change::
- :tags: sql
- :tickets: 1566
-
- an executemany() now requires that all bound parameter
- sets require that all keys are present which are
- present in the first bound parameter set. The structure
- and behavior of an insert/update statement is very much
- determined by the first parameter set, including which
- defaults are going to fire off, and a minimum of
- guesswork is performed with all the rest so that performance
- is not impacted. For this reason defaults would otherwise
- silently "fail" for missing parameters, so this is now guarded
- against.
-
- .. change::
- :tags: sql
- :tickets:
-
- returning() support is native to insert(), update(),
- delete(). Implementations of varying levels of
- functionality exist for PostgreSQL, Firebird, MSSQL and
- Oracle. returning() can be called explicitly with column
- expressions which are then returned in the resultset,
- usually via fetchone() or first().
-
- insert() constructs will also use RETURNING implicitly to
- get newly generated primary key values, if the database
- version in use supports it (a version number check is
- performed). This occurs if no end-user returning() was
- specified.
-
- .. change::
- :tags: sql
- :tickets: 1665
-
- union(), intersect(), except() and other "compound" types
- of statements have more consistent behavior w.r.t.
- parenthesizing. Each compound element embedded within
- another will now be grouped with parenthesis - previously,
- the first compound element in the list would not be grouped,
- as SQLite doesn't like a statement to start with
- parenthesis. However, PostgreSQL in particular has
- precedence rules regarding INTERSECT, and it is
- more consistent for parenthesis to be applied equally
- to all sub-elements. So now, the workaround for SQLite
- is also what the workaround for PG was previously -
- when nesting compound elements, the first one usually needs
- ".alias().select()" called on it to wrap it inside
- of a subquery.
-
- .. change::
- :tags: sql
- :tickets: 1579
-
- insert() and update() constructs can now embed bindparam()
- objects using names that match the keys of columns. These
- bind parameters will circumvent the usual route to those
- keys showing up in the VALUES or SET clause of the generated
- SQL.
-
- .. change::
- :tags: sql
- :tickets: 1524
-
- the Binary type now returns data as a Python string
- (or a "bytes" type in Python 3), instead of the built-
- in "buffer" type. This allows symmetric round trips
- of binary data.
-
- .. change::
- :tags: sql
- :tickets:
-
- Added a tuple_() construct, allows sets of expressions
- to be compared to another set, typically with IN against
- composite primary keys or similar. Also accepts an
- IN with multiple columns. The "scalar select can
- have only one column" error message is removed - will
- rely upon the database to report problems with
- col mismatch.
-
- .. change::
- :tags: sql
- :tickets:
-
- User-defined "default" and "onupdate" callables which
- accept a context should now call upon
- "context.current_parameters" to get at the dictionary
- of bind parameters currently being processed. This
- dict is available in the same way regardless of
- single-execute or executemany-style statement execution.
-
- .. change::
- :tags: sql
- :tickets: 1428
-
- multi-part schema names, i.e. with dots such as
- "dbo.master", are now rendered in select() labels
- with underscores for dots, i.e. "dbo_master_table_column".
- This is a "friendly" label that behaves better
- in result sets.
-
- .. change::
- :tags: sql
- :tickets:
-
- removed needless "counter" behavior with select()
- labelnames that match a column name in the table,
- i.e. generates "tablename_id" for "id", instead of
- "tablename_id_1" in an attempt to avoid naming
- conflicts, when the table has a column actually
- named "tablename_id" - this is because
- the labeling logic is always applied to all columns
- so a naming conflict will never occur.
-
- .. change::
- :tags: sql
- :tickets: 1628
-
- calling expr.in_([]), i.e. with an empty list, emits a warning
- before issuing the usual "expr != expr" clause. The
- "expr != expr" can be very expensive, and it's preferred
- that the user not issue in_() if the list is empty,
- instead simply not querying, or modifying the criterion
- as appropriate for more complex situations.
-
- .. change::
- :tags: sql
- :tickets:
-
- Added "execution_options()" to select()/text(), which set the
- default options for the Connection. See the note in "engines".
-
- .. change::
- :tags: sql
- :tickets: 1131
-
- Deprecated or removed:
- * "scalar" flag on select() is removed, use
- select.as_scalar().
- * "shortname" attribute on bindparam() is removed.
- * postgres_returning, firebird_returning flags on
- insert(), update(), delete() are deprecated, use
- the new returning() method.
- * fold_equivalents flag on join is deprecated (will remain
- until is implemented)
-
- .. change::
- :tags: engines
- :tickets: 443
-
- transaction isolation level may be specified with
- create_engine(... isolation_level="..."); available on
- postgresql and sqlite.
-
- .. change::
- :tags: engines
- :tickets:
-
- Connection has execution_options(), generative method
- which accepts keywords that affect how the statement
- is executed w.r.t. the DBAPI. Currently supports
- "stream_results", causes psycopg2 to use a server
- side cursor for that statement, as well as
- "autocommit", which is the new location for the "autocommit"
- option from select() and text(). select() and
- text() also have .execution_options() as well as
- ORM Query().
-
- .. change::
- :tags: engines
- :tickets: 1630
-
- fixed the import for entrypoint-driven dialects to
- not rely upon silly tb_info trick to determine import
- error status.
-
- .. change::
- :tags: engines
- :tickets:
-
- added first() method to ResultProxy, returns first row and
- closes result set immediately.
-
- .. change::
- :tags: engines
- :tickets:
-
- RowProxy objects are now pickleable, i.e. the object returned
- by result.fetchone(), result.fetchall() etc.
-
- .. change::
- :tags: engines
- :tickets:
-
- RowProxy no longer has a close() method, as the row no longer
- maintains a reference to the parent. Call close() on
- the parent ResultProxy instead, or use autoclose.
-
- .. change::
- :tags: engines
- :tickets: 1586
-
- ResultProxy internals have been overhauled to greatly reduce
- method call counts when fetching columns. Can provide a large
- speed improvement (up to more than 100%) when fetching large
- result sets. The improvement is larger when fetching columns
- that have no type-level processing applied and when using
- results as tuples (instead of as dictionaries). Many
- thanks to Elixir's Gaëtan de Menten for this dramatic
- improvement !
-
- .. change::
- :tags: engines
- :tickets:
-
- Databases which rely upon postfetch of "last inserted id"
- to get at a generated sequence value (i.e. MySQL, MS-SQL)
- now work correctly when there is a composite primary key
- where the "autoincrement" column is not the first primary
- key column in the table.
-
- .. change::
- :tags: engines
- :tickets:
-
- the last_inserted_ids() method has been renamed to the
- descriptor "inserted_primary_key".
-
- .. change::
- :tags: engines
- :tickets: 1554
-
- setting echo=False on create_engine() now sets the loglevel
- to WARN instead of NOTSET. This so that logging can be
- disabled for a particular engine even if logging
- for "sqlalchemy.engine" is enabled overall. Note that the
- default setting of "echo" is `None`.
-
- .. change::
- :tags: engines
- :tickets:
-
- ConnectionProxy now has wrapper methods for all transaction
- lifecycle events, including begin(), rollback(), commit()
- begin_nested(), begin_prepared(), prepare(), release_savepoint(),
- etc.
-
- .. change::
- :tags: engines
- :tickets:
-
- Connection pool logging now uses both INFO and DEBUG
- log levels for logging. INFO is for major events such
- as invalidated connections, DEBUG for all the acquire/return
- logging. `echo_pool` can be False, None, True or "debug"
- the same way as `echo` works.
-
- .. change::
- :tags: engines
- :tickets: 1621
-
- All pyodbc-dialects now support extra pyodbc-specific
- kw arguments 'ansi', 'unicode_results', 'autocommit'.
-
- .. change::
- :tags: engines
- :tickets:
-
- the "threadlocal" engine has been rewritten and simplified
- and now supports SAVEPOINT operations.
-
- .. change::
- :tags: engines
- :tickets:
-
- deprecated or removed
- * result.last_inserted_ids() is deprecated. Use
- result.inserted_primary_key
- * dialect.get_default_schema_name(connection) is now
- public via dialect.default_schema_name.
- * the "connection" argument from engine.transaction() and
- engine.run_callable() is removed - Connection itself
- now has those methods. All four methods accept
- \*args and \**kwargs which are passed to the given callable,
- as well as the operating connection.
-
- .. change::
- :tags: schema
- :tickets: 1541
-
- the `__contains__()` method of `MetaData` now accepts
- strings or `Table` objects as arguments. If given
- a `Table`, the argument is converted to `table.key` first,
- i.e. "[schemaname.]<tablename>"
-
- .. change::
- :tags: schema
- :tickets:
-
- deprecated MetaData.connect() and
- ThreadLocalMetaData.connect() have been removed - send
- the "bind" attribute to bind a metadata.
-
- .. change::
- :tags: schema
- :tickets:
-
- deprecated metadata.table_iterator() method removed (use
- sorted_tables)
-
- .. change::
- :tags: schema
- :tickets:
-
- deprecated PassiveDefault - use DefaultClause.
-
- .. change::
- :tags: schema
- :tickets:
-
- the "metadata" argument is removed from DefaultGenerator
- and subclasses, but remains locally present on Sequence,
- which is a standalone construct in DDL.
-
- .. change::
- :tags: schema
- :tickets:
-
- Removed public mutability from Index and Constraint
- objects:
-
- * ForeignKeyConstraint.append_element()
- * Index.append_column()
- * UniqueConstraint.append_column()
- * PrimaryKeyConstraint.add()
- * PrimaryKeyConstraint.remove()
-
- These should be constructed declaratively (i.e. in one
- construction).
-
- .. change::
- :tags: schema
- :tickets: 1545
-
- The "start" and "increment" attributes on Sequence now
- generate "START WITH" and "INCREMENT BY" by default,
- on Oracle and PostgreSQL. Firebird doesn't support
- these keywords right now.
-
- .. change::
- :tags: schema
- :tickets:
-
- UniqueConstraint, Index, PrimaryKeyConstraint all accept
- lists of column names or column objects as arguments.
-
- .. change::
- :tags: schema
- :tickets:
-
- Other removed things:
- - Table.key (no idea what this was for)
- - Table.primary_key is not assignable - use
- table.append_constraint(PrimaryKeyConstraint(...))
- - Column.bind (get via column.table.bind)
- - Column.metadata (get via column.table.metadata)
- - Column.sequence (use column.default)
- - ForeignKey(constraint=some_parent) (is now private _constraint)
-
- .. change::
- :tags: schema
- :tickets:
-
- The use_alter flag on ForeignKey is now a shortcut option
- for operations that can be hand-constructed using the
- DDL() event system. A side effect of this refactor is
- that ForeignKeyConstraint objects with use_alter=True
- will *not* be emitted on SQLite, which does not support
- ALTER for foreign keys.
-
- .. change::
- :tags: schema
- :tickets: 1605
-
- ForeignKey and ForeignKeyConstraint objects now correctly
- copy() all their public keyword arguments.
-
- .. change::
- :tags: reflection/inspection
- :tickets:
-
- Table reflection has been expanded and generalized into
- a new API called "sqlalchemy.engine.reflection.Inspector".
- The Inspector object provides fine-grained information about
- a wide variety of schema information, with room for expansion,
- including table names, column names, view definitions, sequences,
- indexes, etc.
-
- .. change::
- :tags: reflection/inspection
- :tickets:
-
- Views are now reflectable as ordinary Table objects. The same
- Table constructor is used, with the caveat that "effective"
- primary and foreign key constraints aren't part of the reflection
- results; these have to be specified explicitly if desired.
-
- .. change::
- :tags: reflection/inspection
- :tickets:
-
- The existing autoload=True system now uses Inspector underneath
- so that each dialect need only return "raw" data about tables
- and other objects - Inspector is the single place that information
- is compiled into Table objects so that consistency is at a maximum.
-
- .. change::
- :tags: ddl
- :tickets:
-
- the DDL system has been greatly expanded. the DDL() class
- now extends the more generic DDLElement(), which forms the basis
- of many new constructs:
-
- - CreateTable()
- - DropTable()
- - AddConstraint()
- - DropConstraint()
- - CreateIndex()
- - DropIndex()
- - CreateSequence()
- - DropSequence()
-
- These support "on" and "execute-at()" just like plain DDL()
- does. User-defined DDLElement subclasses can be created and
- linked to a compiler using the sqlalchemy.ext.compiler extension.
-
- .. change::
- :tags: ddl
- :tickets:
-
- The signature of the "on" callable passed to DDL() and
- DDLElement() is revised as follows:
-
- ddl
- the DDLElement object itself
- event
- the string event name.
- target
- previously "schema_item", the Table or MetaData object triggering the event.
- connection
- the Connection object in use for the operation.
- \**kw
- keyword arguments. In the case of MetaData before/after
- create/drop, the list of Table objects for which
- CREATE/DROP DDL is to be issued is passed as the kw
- argument "tables". This is necessary for metadata-level
- DDL that is dependent on the presence of specific tables.
-
- The "schema_item" attribute of DDL has been renamed to
- "target".
-
- .. change::
- :tags: dialect, refactor
- :tickets:
-
- Dialect modules are now broken into database dialects
- plus DBAPI implementations. Connect URLs are now
- preferred to be specified using dialect+driver://...,
- i.e. "mysql+mysqldb://scott:tiger@localhost/test". See
- the 0.6 documentation for examples.
-
- .. change::
- :tags: dialect, refactor
- :tickets:
-
- the setuptools entrypoint for external dialects is now
- called "sqlalchemy.dialects".
-
- .. change::
- :tags: dialect, refactor
- :tickets:
-
- the "owner" keyword argument is removed from Table. Use
- "schema" to represent any namespaces to be prepended to
- the table name.
-
- .. change::
- :tags: dialect, refactor
- :tickets:
-
- server_version_info becomes a static attribute.
-
- .. change::
- :tags: dialect, refactor
- :tickets:
-
- dialects receive an initialize() event on initial
- connection to determine connection properties.
-
- .. change::
- :tags: dialect, refactor
- :tickets:
-
- dialects receive a visit_pool event have an opportunity
- to establish pool listeners.
-
- .. change::
- :tags: dialect, refactor
- :tickets:
-
- cached TypeEngine classes are cached per-dialect class
- instead of per-dialect.
-
- .. change::
- :tags: dialect, refactor
- :tickets:
-
- new UserDefinedType should be used as a base class for
- new types, which preserves the 0.5 behavior of
- get_col_spec().
-
- .. change::
- :tags: dialect, refactor
- :tickets:
-
- The result_processor() method of all type classes now
- accepts a second argument "coltype", which is the DBAPI
- type argument from cursor.description. This argument
- can help some types decide on the most efficient processing
- of result values.
-
- .. change::
- :tags: dialect, refactor
- :tickets:
-
- Deprecated Dialect.get_params() removed.
-
- .. change::
- :tags: dialect, refactor
- :tickets:
-
- Dialect.get_rowcount() has been renamed to a descriptor
- "rowcount", and calls cursor.rowcount directly. Dialects
- which need to hardwire a rowcount in for certain calls
- should override the method to provide different behavior.
-
- .. change::
- :tags: dialect, refactor
- :tickets: 1566
-
- DefaultRunner and subclasses have been removed. The job
- of this object has been simplified and moved into
- ExecutionContext. Dialects which support sequences should
- add a `fire_sequence()` method to their execution context
- implementation.
-
- .. change::
- :tags: dialect, refactor
- :tickets:
-
- Functions and operators generated by the compiler now use
- (almost) regular dispatch functions of the form
- "visit_<opname>" and "visit_<funcname>_fn" to provide
- customed processing. This replaces the need to copy the
- "functions" and "operators" dictionaries in compiler
- subclasses with straightforward visitor methods, and also
- allows compiler subclasses complete control over
- rendering, as the full _Function or _BinaryExpression
- object is passed in.
-
- .. change::
- :tags: postgresql
- :tickets:
-
- New dialects: pg8000, zxjdbc, and pypostgresql
- on py3k.
-
- .. change::
- :tags: postgresql
- :tickets:
-
- The "postgres" dialect is now named "postgresql" !
- Connection strings look like:
-
- postgresql://scott:tiger@localhost/test
- postgresql+pg8000://scott:tiger@localhost/test
-
- The "postgres" name remains for backwards compatibility
- in the following ways:
-
- - There is a "postgres.py" dummy dialect which
- allows old URLs to work, i.e.
- postgres://scott:tiger@localhost/test
-
- - The "postgres" name can be imported from the old
- "databases" module, i.e. "from
- sqlalchemy.databases import postgres" as well as
- "dialects", "from sqlalchemy.dialects.postgres
- import base as pg", will send a deprecation
- warning.
-
- - Special expression arguments are now named
- "postgresql_returning" and "postgresql_where", but
- the older "postgres_returning" and
- "postgres_where" names still work with a
- deprecation warning.
-
- .. change::
- :tags: postgresql
- :tickets:
-
- "postgresql_where" now accepts SQL expressions which
- can also include literals, which will be quoted as needed.
-
- .. change::
- :tags: postgresql
- :tickets:
-
- The psycopg2 dialect now uses psycopg2's "unicode extension"
- on all new connections, which allows all String/Text/etc.
- types to skip the need to post-process bytestrings into
- unicode (an expensive step due to its volume). Other
- dialects which return unicode natively (pg8000, zxjdbc)
- also skip unicode post-processing.
-
- .. change::
- :tags: postgresql
- :tickets: 1511
-
- Added new ENUM type, which exists as a schema-level
- construct and extends the generic Enum type. Automatically
- associates itself with tables and their parent metadata
- to issue the appropriate CREATE TYPE/DROP TYPE
- commands as needed, supports unicode labels, supports
- reflection.
-
- .. change::
- :tags: postgresql
- :tickets:
-
- INTERVAL supports an optional "precision" argument
- corresponding to the argument that PG accepts.
-
- .. change::
- :tags: postgresql
- :tickets:
-
- using new dialect.initialize() feature to set up
- version-dependent behavior.
-
- .. change::
- :tags: postgresql
- :tickets: 1279
-
- somewhat better support for % signs in table/column names;
- psycopg2 can't handle a bind parameter name of
- %(foobar)s however and SQLA doesn't want to add overhead
- just to treat that one non-existent use case.
-
- .. change::
- :tags: postgresql
- :tickets: 1516
-
- Inserting NULL into a primary key + foreign key column
- will allow the "not null constraint" error to raise,
- not an attempt to execute a nonexistent "col_id_seq"
- sequence.
-
- .. change::
- :tags: postgresql
- :tickets:
-
- autoincrement SELECT statements, i.e. those which
- select from a procedure that modifies rows, now work
- with server-side cursor mode (the named cursor isn't
- used for such statements.)
-
- .. change::
- :tags: postgresql
- :tickets: 1636
-
- postgresql dialect can properly detect pg "devel" version
- strings, i.e. "8.5devel"
-
- .. change::
- :tags: postgresql
- :tickets: 1619
-
- The psycopg2 now respects the statement option
- "stream_results". This option overrides the connection setting
- "server_side_cursors". If true, server side cursors will be
- used for the statement. If false, they will not be used, even
- if "server_side_cursors" is true on the
- connection.
-
- .. change::
- :tags: mysql
- :tickets:
-
- New dialects: oursql, a new native dialect,
- MySQL Connector/Python, a native Python port of MySQLdb,
- and of course zxjdbc on Jython.
-
- .. change::
- :tags: mysql
- :tickets:
-
- VARCHAR/NVARCHAR will not render without a length, raises
- an error before passing to MySQL. Doesn't impact
- CAST since VARCHAR is not allowed in MySQL CAST anyway,
- the dialect renders CHAR/NCHAR in those cases.
-
- .. change::
- :tags: mysql
- :tickets:
-
- all the _detect_XXX() functions now run once underneath
- dialect.initialize()
-
- .. change::
- :tags: mysql
- :tickets: 1279
-
- somewhat better support for % signs in table/column names;
- MySQLdb can't handle % signs in SQL when executemany() is used,
- and SQLA doesn't want to add overhead just to treat that one
- non-existent use case.
-
- .. change::
- :tags: mysql
- :tickets:
-
- the BINARY and MSBinary types now generate "BINARY" in all
- cases. Omitting the "length" parameter will generate
- "BINARY" with no length. Use BLOB to generate an unlengthed
- binary column.
-
- .. change::
- :tags: mysql
- :tickets:
-
- the "quoting='quoted'" argument to MSEnum/ENUM is deprecated.
- It's best to rely upon the automatic quoting.
-
- .. change::
- :tags: mysql
- :tickets:
-
- ENUM now subclasses the new generic Enum type, and also handles
- unicode values implicitly, if the given labelnames are unicode
- objects.
-
- .. change::
- :tags: mysql
- :tickets: 1539
-
- a column of type TIMESTAMP now defaults to NULL if
- "nullable=False" is not passed to Column(), and no default
- is present. This is now consistent with all other types,
- and in the case of TIMESTAMP explicitly renders "NULL"
- due to MySQL's "switching" of default nullability
- for TIMESTAMP columns.
-
- .. change::
- :tags: oracle
- :tickets:
-
- unit tests pass 100% with cx_oracle !
-
- .. change::
- :tags: oracle
- :tickets:
-
- support for cx_Oracle's "native unicode" mode which does
- not require NLS_LANG to be set. Use the latest 5.0.2 or
- later of cx_oracle.
-
- .. change::
- :tags: oracle
- :tickets:
-
- an NCLOB type is added to the base types.
-
- .. change::
- :tags: oracle
- :tickets:
-
- use_ansi=False won't leak into the FROM/WHERE clause of
- a statement that's selecting from a subquery that also
- uses JOIN/OUTERJOIN.
-
- .. change::
- :tags: oracle
- :tickets: 1467
-
- added native INTERVAL type to the dialect. This supports
- only the DAY TO SECOND interval type so far due to lack
- of support in cx_oracle for YEAR TO MONTH.
-
- .. change::
- :tags: oracle
- :tickets:
-
- usage of the CHAR type results in cx_oracle's
- FIXED_CHAR dbapi type being bound to statements.
-
- .. change::
- :tags: oracle
- :tickets: 885
-
- the Oracle dialect now features NUMBER which intends
- to act justlike Oracle's NUMBER type. It is the primary
- numeric type returned by table reflection and attempts
- to return Decimal()/float/int based on the precision/scale
- parameters.
-
- .. change::
- :tags: oracle
- :tickets:
-
- func.char_length is a generic function for LENGTH
-
- .. change::
- :tags: oracle
- :tickets:
-
- ForeignKey() which includes onupdate=<value> will emit a
- warning, not emit ON UPDATE CASCADE which is unsupported
- by oracle
-
- .. change::
- :tags: oracle
- :tickets:
-
- the keys() method of RowProxy() now returns the result
- column names *normalized* to be SQLAlchemy case
- insensitive names. This means they will be lower case for
- case insensitive names, whereas the DBAPI would normally
- return them as UPPERCASE names. This allows row keys() to
- be compatible with further SQLAlchemy operations.
-
- .. change::
- :tags: oracle
- :tickets:
-
- using new dialect.initialize() feature to set up
- version-dependent behavior.
-
- .. change::
- :tags: oracle
- :tickets: 1125
-
- using types.BigInteger with Oracle will generate
- NUMBER(19)
-
- .. change::
- :tags: oracle
- :tickets:
-
- "case sensitivity" feature will detect an all-lowercase
- case-sensitive column name during reflect and add
- "quote=True" to the generated Column, so that proper
- quoting is maintained.
-
- .. change::
- :tags: firebird
- :tickets:
-
- the keys() method of RowProxy() now returns the result
- column names *normalized* to be SQLAlchemy case
- insensitive names. This means they will be lower case for
- case insensitive names, whereas the DBAPI would normally
- return them as UPPERCASE names. This allows row keys() to
- be compatible with further SQLAlchemy operations.
-
- .. change::
- :tags: firebird
- :tickets:
-
- using new dialect.initialize() feature to set up
- version-dependent behavior.
-
- .. change::
- :tags: firebird
- :tickets:
-
- "case sensitivity" feature will detect an all-lowercase
- case-sensitive column name during reflect and add
- "quote=True" to the generated Column, so that proper
- quoting is maintained.
-
- .. change::
- :tags: mssql
- :tickets:
-
- MSSQL + Pyodbc + FreeTDS now works for the most part,
- with possible exceptions regarding binary data as well as
- unicode schema identifiers.
-
- .. change::
- :tags: mssql
- :tickets:
-
- the "has_window_funcs" flag is removed. LIMIT/OFFSET
- usage will use ROW NUMBER as always, and if on an older
- version of SQL Server, the operation fails. The behavior
- is exactly the same except the error is raised by SQL
- server instead of the dialect, and no flag setting is
- required to enable it.
-
- .. change::
- :tags: mssql
- :tickets:
-
- the "auto_identity_insert" flag is removed. This feature
- always takes effect when an INSERT statement overrides a
- column that is known to have a sequence on it. As with
- "has_window_funcs", if the underlying driver doesn't
- support this, then you can't do this operation in any
- case, so there's no point in having a flag.
-
- .. change::
- :tags: mssql
- :tickets:
-
- using new dialect.initialize() feature to set up
- version-dependent behavior.
-
- .. change::
- :tags: mssql
- :tickets:
-
- removed references to sequence which is no longer used.
- implicit identities in mssql work the same as implicit
- sequences on any other dialects. Explicit sequences are
- enabled through the use of "default=Sequence()". See
- the MSSQL dialect documentation for more information.
-
- .. change::
- :tags: sqlite
- :tickets:
-
- DATE, TIME and DATETIME types can now take optional storage_format
- and regexp argument. storage_format can be used to store those types
- using a custom string format. regexp allows to use a custom regular
- expression to match string values from the database.
-
- .. change::
- :tags: sqlite
- :tickets:
-
- Time and DateTime types now use by a default a stricter regular
- expression to match strings from the database. Use the regexp
- argument if you are using data stored in a legacy format.
-
- .. change::
- :tags: sqlite
- :tickets:
-
- __legacy_microseconds__ on SQLite Time and DateTime types is not
- supported anymore. You should use the storage_format argument
- instead.
-
- .. change::
- :tags: sqlite
- :tickets:
-
- Date, Time and DateTime types are now stricter in what they accept as
- bind parameters: Date type only accepts date objects (and datetime
- ones, because they inherit from date), Time only accepts time
- objects, and DateTime only accepts date and datetime objects.
-
- .. change::
- :tags: sqlite
- :tickets: 1016
-
- Table() supports a keyword argument "sqlite_autoincrement", which
- applies the SQLite keyword "AUTOINCREMENT" to the single integer
- primary key column when generating DDL. Will prevent generation of
- a separate PRIMARY KEY constraint.
-
- .. change::
- :tags: types
- :tickets:
-
- The construction of types within dialects has been totally
- overhauled. Dialects now define publicly available types
- as UPPERCASE names exclusively, and internal implementation
- types using underscore identifiers (i.e. are private).
- The system by which types are expressed in SQL and DDL
- has been moved to the compiler system. This has the
- effect that there are much fewer type objects within
- most dialects. A detailed document on this architecture
- for dialect authors is in
- lib/sqlalchemy/dialects/type_migration_guidelines.txt .
-
- .. change::
- :tags: types
- :tickets:
-
- Types no longer make any guesses as to default
- parameters. In particular, Numeric, Float, NUMERIC,
- FLOAT, DECIMAL don't generate any length or scale unless
- specified.
-
- .. change::
- :tags: types
- :tickets: 1664
-
- types.Binary is renamed to types.LargeBinary, it only
- produces BLOB, BYTEA, or a similar "long binary" type.
- New base BINARY and VARBINARY
- types have been added to access these MySQL/MS-SQL specific
- types in an agnostic way.
-
- .. change::
- :tags: types
- :tickets:
-
- String/Text/Unicode types now skip the unicode() check
- on each result column value if the dialect has
- detected the DBAPI as returning Python unicode objects
- natively. This check is issued on first connect
- using "SELECT CAST 'some text' AS VARCHAR(10)" or
- equivalent, then checking if the returned object
- is a Python unicode. This allows vast performance
- increases for native-unicode DBAPIs, including
- pysqlite/sqlite3, psycopg2, and pg8000.
-
- .. change::
- :tags: types
- :tickets:
-
- Most types result processors have been checked for possible speed
- improvements. Specifically, the following generic types have been
- optimized, resulting in varying speed improvements:
- Unicode, PickleType, Interval, TypeDecorator, Binary.
- Also the following dbapi-specific implementations have been improved:
- Time, Date and DateTime on Sqlite, ARRAY on PostgreSQL,
- Time on MySQL, Numeric(as_decimal=False) on MySQL, oursql and
- pypostgresql, DateTime on cx_oracle and LOB-based types on cx_oracle.
-
- .. change::
- :tags: types
- :tickets:
-
- Reflection of types now returns the exact UPPERCASE
- type within types.py, or the UPPERCASE type within
- the dialect itself if the type is not a standard SQL
- type. This means reflection now returns more accurate
- information about reflected types.
-
- .. change::
- :tags: types
- :tickets: 1511, 1109
-
- Added a new Enum generic type. Enum is a schema-aware object
- to support databases which require specific DDL in order to
- use enum or equivalent; in the case of PG it handles the
- details of `CREATE TYPE`, and on other databases without
- native enum support will by generate VARCHAR + an inline CHECK
- constraint to enforce the enum.
-
- .. change::
- :tags: types
- :tickets: 1467
-
- The Interval type includes a "native" flag which controls
- if native INTERVAL types (postgresql + oracle) are selected
- if available, or not. "day_precision" and "second_precision"
- arguments are also added which propagate as appropriately
- to these native types. Related to.
-
- .. change::
- :tags: types
- :tickets: 1589
-
- The Boolean type, when used on a backend that doesn't
- have native boolean support, will generate a CHECK
- constraint "col IN (0, 1)" along with the int/smallint-
- based column type. This can be switched off if
- desired with create_constraint=False.
- Note that MySQL has no native boolean *or* CHECK constraint
- support so this feature isn't available on that platform.
-
- .. change::
- :tags: types
- :tickets:
-
- PickleType now uses == for comparison of values when
- mutable=True, unless the "comparator" argument with a
- comparison function is specified to the type. Objects
- being pickled will be compared based on identity (which
- defeats the purpose of mutable=True) if __eq__() is not
- overridden or a comparison function is not provided.
-
- .. change::
- :tags: types
- :tickets:
-
- The default "precision" and "scale" arguments of Numeric
- and Float have been removed and now default to None.
- NUMERIC and FLOAT will be rendered with no numeric
- arguments by default unless these values are provided.
-
- .. change::
- :tags: types
- :tickets:
-
- AbstractType.get_search_list() is removed - the games
- that was used for are no longer necessary.
-
- .. change::
- :tags: types
- :tickets: 1125
-
- Added a generic BigInteger type, compiles to
- BIGINT or NUMBER(19).
-
- .. change::
- :tags: types
- :tickets:
-
- sqlsoup has been overhauled to explicitly support an 0.5 style
- session, using autocommit=False, autoflush=True. Default
- behavior of SQLSoup now requires the usual usage of commit()
- and rollback(), which have been added to its interface. An
- explicit Session or scoped_session can be passed to the
- constructor, allowing these arguments to be overridden.
-
- .. change::
- :tags: types
- :tickets:
-
- sqlsoup db.<sometable>.update() and delete() now call
- query(cls).update() and delete(), respectively.
-
- .. change::
- :tags: types
- :tickets:
-
- sqlsoup now has execute() and connection(), which call upon
- the Session methods of those names, ensuring that the bind is
- in terms of the SqlSoup object's bind.
-
- .. change::
- :tags: types
- :tickets:
-
- sqlsoup objects no longer have the 'query' attribute - it's
- not needed for sqlsoup's usage paradigm and it gets in the
- way of a column that is actually named 'query'.
-
- .. change::
- :tags: types
- :tickets: 1259
-
- The signature of the proxy_factory callable passed to
- association_proxy is now (lazy_collection, creator,
- value_attr, association_proxy), adding a fourth argument
- that is the parent AssociationProxy argument. Allows
- serializability and subclassing of the built in collections.
-
- .. change::
- :tags: types
- :tickets: 1372
-
- association_proxy now has basic comparator methods .any(),
- .has(), .contains(), ==, !=, thanks to Scott Torborg.
+++ /dev/null
-=============
-0.7 Changelog
-=============
-
-
-.. changelog::
- :version: 0.7.11
- :released:
-
- .. change::
- :tags: bug, engine
- :tickets: 2851
- :versions: 0.8.3, 0.9.0b1
-
- The regexp used by the :func:`~sqlalchemy.engine.url.make_url` function now parses
- ipv6 addresses, e.g. surrounded by brackets.
-
- .. change::
- :tags: bug, orm
- :tickets: 2807
- :versions: 0.8.3, 0.9.0b1
-
- Fixed bug where list instrumentation would fail to represent a
- setslice of ``[0:0]`` correctly, which in particular could occur
- when using ``insert(0, item)`` with the association proxy. Due
- to some quirk in Python collections, the issue was much more likely
- with Python 3 rather than 2.
-
- .. change::
- :tags: bug, sql
- :tickets: 2801
- :versions: 0.8.3, 0.9.0b1
-
- Fixed regression dating back to 0.7.9 whereby the name of a CTE might
- not be properly quoted if it was referred to in multiple FROM clauses.
-
- .. change::
- :tags: mysql, bug
- :tickets: 2791
- :versions: 0.8.3, 0.9.0b1
-
- Updates to MySQL reserved words for versions 5.5, 5.6, courtesy
- Hanno Schlichting.
-
- .. change::
- :tags: sql, bug, cte
- :tickets: 2783
- :versions: 0.8.3, 0.9.0b1
-
- Fixed bug in common table expression system where if the CTE were
- used only as an ``alias()`` construct, it would not render using the
- WITH keyword.
-
- .. change::
- :tags: bug, sql
- :tickets: 2784
- :versions: 0.8.3, 0.9.0b1
-
- Fixed bug in :class:`.CheckConstraint` DDL where the "quote" flag from a
- :class:`_schema.Column` object would not be propagated.
-
- .. change::
- :tags: bug, orm
- :tickets: 2699
- :versions: 0.8.1
-
- Fixed bug when a query of the form:
- ``query(SubClass).options(subqueryload(Baseclass.attrname))``,
- where ``SubClass`` is a joined inh of ``BaseClass``,
- would fail to apply the ``JOIN`` inside the subquery
- on the attribute load, producing a cartesian product.
- The populated results still tended to be correct as additional
- rows are just ignored, so this issue may be present as a
- performance degradation in applications that are
- otherwise working correctly.
-
- .. change::
- :tags: bug, orm
- :tickets: 2689
- :versions: 0.8.1
-
- Fixed bug in unit of work whereby a joined-inheritance
- subclass could insert the row for the "sub" table
- before the parent table, if the two tables had no
- ForeignKey constraints set up between them.
-
- .. change::
- :tags: feature, postgresql
- :tickets: 2676
- :versions: 0.8.0
-
- Added support for PostgreSQL's traditional SUBSTRING
- function syntax, renders as "SUBSTRING(x FROM y FOR z)"
- when regular ``func.substring()`` is used.
- Courtesy Gunnlaugur Þór Briem.
-
- .. change::
- :tags: bug, tests
- :tickets: 2669
- :pullreq: 41
-
- Fixed an import of "logging" in test_execute which was not
- working on some linux platforms.
-
- .. change::
- :tags: bug, orm
- :tickets: 2674
-
- Improved the error message emitted when a "backref loop" is detected,
- that is when an attribute event triggers a bidirectional
- assignment between two other attributes with no end.
- This condition can occur not just when an object of the wrong
- type is assigned, but also when an attribute is mis-configured
- to backref into an existing backref pair.
-
- .. change::
- :tags: bug, orm
- :tickets: 2674
-
- A warning is emitted when a MapperProperty is assigned to a mapper
- that replaces an existing property, if the properties in question
- aren't plain column-based properties. Replacement of relationship
- properties is rarely (ever?) what is intended and usually refers to a
- mapper mis-configuration. This will also warn if a backref configures
- itself on top of an existing one in an inheritance relationship
- (which is an error in 0.8).
-
-.. changelog::
- :version: 0.7.10
- :released: Thu Feb 7 2013
-
- .. change::
- :tags: engine, bug
- :tickets: 2604
- :versions: 0.8.0b2
-
- Fixed :meth:`_schema.MetaData.reflect` to correctly use
- the given :class:`_engine.Connection`, if given, without
- opening a second connection from that connection's
- :class:`_engine.Engine`.
-
- .. change::
- :tags: mssql, bug
- :tickets:2607
- :versions: 0.8.0b2
-
- Fixed bug whereby using "key" with Column
- in conjunction with "schema" for the owning
- Table would fail to locate result rows due
- to the MSSQL dialect's "schema rendering"
- logic's failure to take .key into account.
-
- .. change::
- :tags: sql, mysql, gae
- :tickets: 2649
-
- Added a conditional import to the ``gaerdbms`` dialect which attempts
- to import rdbms_apiproxy vs. rdbms_googleapi to work
- on both dev and production platforms. Also now honors the
- ``instance`` attribute. Courtesy Sean Lynch. Also backported
- enhancements to allow username/password as well as
- fixing error code interpretation from 0.8.
-
- .. change::
- :tags: sql, bug
- :tickets: 2594, 2584
-
- Backported adjustment to ``__repr__`` for
- :class:`.TypeDecorator` to 0.7, allows :class:`.PickleType`
- to produce a clean ``repr()`` to help with Alembic.
-
- .. change::
- :tags: sql, bug
- :tickets: 2643
-
- Fixed bug where :meth:`_schema.Table.tometadata` would fail if a
- :class:`_schema.Column` had both a foreign key as well as an
- alternate ".key" name for the column.
-
- .. change::
- :tags: mssql, bug
- :tickets: 2638
-
- Added a Py3K conditional around unnecessary .decode()
- call in mssql information schema, fixes reflection
- in Py3k.
-
- .. change::
- :tags: orm, bug
- :tickets: 2650
-
- Fixed potential memory leak which could occur if an
- arbitrary number of :class:`.sessionmaker` objects
- were created. The anonymous subclass created by
- the sessionmaker, when dereferenced, would not be garbage
- collected due to remaining class-level references from the
- event package. This issue also applies to any custom system
- that made use of ad-hoc subclasses in conjunction with
- an event dispatcher.
-
- .. change::
- :tags: orm, bug
- :tickets: 2640
-
- :meth:`_query.Query.merge_result` can now load rows from an outer join
- where an entity may be ``None`` without throwing an error.
-
- .. change::
- :tags: sqlite, bug
- :tickets: 2568
- :versions: 0.8.0b2
-
- More adjustment to this SQLite related issue which was released in
- 0.7.9, to intercept legacy SQLite quoting characters when reflecting
- foreign keys. In addition to intercepting double quotes, other
- quoting characters such as brackets, backticks, and single quotes
- are now also intercepted.
-
- .. change::
- :tags: sql, bug
- :tickets: 2631
- :versions: 0.8.0b2
-
- Fixed bug where using server_onupdate=<FetchedValue|DefaultClause>
- without passing the "for_update=True" flag would apply the default
- object to the server_default, blowing away whatever was there.
- The explicit for_update=True argument shouldn't be needed with this usage
- (especially since the documentation shows an example without it being
- used) so it is now arranged internally using a copy of the given default
- object, if the flag isn't set to what corresponds to that argument.
-
- .. change::
- :tags: oracle, bug
- :tickets: 2620
-
- The Oracle LONG type, while an unbounded text type, does not appear
- to use the cx_Oracle.LOB type when result rows are returned,
- so the dialect has been repaired to exclude LONG from
- having cx_Oracle.LOB filtering applied.
-
- .. change::
- :tags: oracle, bug
- :tickets: 2611
-
- Repaired the usage of ``.prepare()`` in conjunction with
- cx_Oracle so that a return value of ``False`` will result
- in no call to ``connection.commit()``, hence avoiding
- "no transaction" errors. Two-phase transactions have
- now been shown to work in a rudimental fashion with
- SQLAlchemy and cx_oracle, however are subject to caveats
- observed with the driver; check the documentation
- for details.
-
- .. change::
- :tags: orm, bug
- :tickets: 2624
-
- The :class:`.MutableComposite` type did not allow for the
- :meth:`.MutableBase.coerce` method to be used, even though
- the code seemed to indicate this intent, so this now works
- and a brief example is added. As a side-effect,
- the mechanics of this event handler have been changed so that
- new :class:`.MutableComposite` types no longer add per-type
- global event handlers. Also in 0.8.0b2.
-
- .. change::
- :tags: orm, bug
- :tickets: 2583
-
- Fixed Session accounting bug whereby replacing
- a deleted object in the identity map with another
- object of the same primary key would raise a
- "conflicting state" error on rollback(),
- if the replaced primary key were established either
- via non-unitofwork-established INSERT statement
- or by primary key switch of another instance.
-
- .. change::
- :tags: oracle, bug
- :tickets: 2561
-
- changed the list of cx_oracle types that are
- excluded from the setinputsizes() step to only include
- STRING and UNICODE; CLOB and NCLOB are removed. This
- is to work around cx_oracle behavior which is broken
- for the executemany() call. In 0.8, this same change
- is applied however it is also configurable via the
- exclude_setinputsizes argument.
-
- .. change::
- :tags: feature, mysql
- :tickets: 2523
-
- Added "raise_on_warnings" flag to OurSQL
- dialect.
-
- .. change::
- :tags: feature, mysql
- :tickets: 2554
-
- Added "read_timeout" flag to MySQLdb
- dialect.
-
-.. changelog::
- :version: 0.7.9
- :released: Mon Oct 01 2012
-
- .. change::
- :tags: orm, bug
- :tickets:
-
- Fixed bug mostly local to new
- AbstractConcreteBase helper where the "type"
- attribute from the superclass would not
- be overridden on the subclass to produce the
- "reserved for base" error message, instead placing
- a do-nothing attribute there. This was inconsistent
- vs. using ConcreteBase as well as all the behavior
- of classical concrete mappings, where the "type"
- column from the polymorphic base would be explicitly
- disabled on subclasses, unless overridden
- explicitly.
-
- .. change::
- :tags: orm, bug
- :tickets:
-
- A warning is emitted when lazy='dynamic'
- is combined with uselist=False. This is an
- exception raise in 0.8.
-
- .. change::
- :tags: orm, bug
- :tickets:
-
- Fixed bug whereby user error in related-object
- assignment could cause recursion overflow if the
- assignment triggered a backref of the same name
- as a bi-directional attribute on the incorrect
- class to the same target. An informative
- error is raised now.
-
- .. change::
- :tags: orm, bug
- :tickets: 2539
-
- Fixed bug where incorrect type information
- would be passed when the ORM would bind the
- "version" column, when using the "version" feature.
- Tests courtesy Daniel Miller.
-
- .. change::
- :tags: orm, bug
- :tickets: 2566
-
- Extra logic has been added to the "flush"
- that occurs within Session.commit(), such that the
- extra state added by an after_flush() or
- after_flush_postexec() hook is also flushed in a
- subsequent flush, before the "commit" completes.
- Subsequent calls to flush() will continue until
- the after_flush hooks stop adding new state.
- An "overflow" counter of 100 is also in place,
- in the event of a broken after_flush() hook
- adding new content each time.
-
- .. change::
- :tags: bug, sql
- :tickets: 2571
-
- Fixed the DropIndex construct to support
- an Index associated with a Table in a remote
- schema.
-
- .. change::
- :tags: bug, sql
- :tickets: 2574
-
- Fixed bug in over() construct whereby
- passing an empty list for either partition_by
- or order_by, as opposed to None, would fail
- to generate correctly.
- Courtesy Gunnlaugur Þór Briem.
-
- .. change::
- :tags: bug, sql
- :tickets: 2521
-
- Fixed CTE bug whereby positional
- bound parameters present in the CTEs themselves
- would corrupt the overall ordering of
- bound parameters. This primarily
- affected SQL Server as the platform with
- positional binds + CTE support.
-
- .. change::
- :tags: bug, sql
- :tickets:
-
- Fixed more un-intuitivenesses in CTEs
- which prevented referring to a CTE in a union
- of itself without it being aliased.
- CTEs now render uniquely
- on name, rendering the outermost CTE of a given
- name only - all other references are rendered
- just as the name. This even includes other
- CTE/SELECTs that refer to different versions
- of the same CTE object, such as a SELECT
- or a UNION ALL of that SELECT. We are
- somewhat loosening the usual link between object
- identity and lexical identity in this case.
- A true name conflict between two unrelated
- CTEs now raises an error.
-
- .. change::
- :tags: bug, sql
- :tickets: 2512
-
- quoting is applied to the column names
- inside the WITH RECURSIVE clause of a
- common table expression according to the
- quoting rules for the originating Column.
-
- .. change::
- :tags: bug, sql
- :tickets: 2518
-
- Fixed regression introduced in 0.7.6
- whereby the FROM list of a SELECT statement
- could be incorrect in certain "clone+replace"
- scenarios.
-
- .. change::
- :tags: bug, sql
- :tickets: 2552
-
- Fixed bug whereby usage of a UNION
- or similar inside of an embedded subquery
- would interfere with result-column targeting,
- in the case that a result-column had the same
- ultimate name as a name inside the embedded
- UNION.
-
- .. change::
- :tags: bug, sql
- :tickets: 2558
-
- Fixed a regression since 0.6 regarding
- result-row targeting. It should be possible
- to use a select() statement with string
- based columns in it, that is
- select(['id', 'name']).select_from('mytable'),
- and have this statement be targetable by
- Column objects with those names; this is the
- mechanism by which
- query(MyClass).from_statement(some_statement)
- works. At some point the specific case of
- using select(['id']), which is equivalent to
- select([literal_column('id')]), stopped working
- here, so this has been re-instated and of
- course tested.
-
- .. change::
- :tags: bug, sql
- :tickets: 2544
-
- Added missing operators is_(), isnot()
- to the ColumnOperators base, so that these long-available
- operators are present as methods like all
- the other operators.
-
- .. change::
- :tags: engine, bug
- :tickets: 2522
-
- Fixed bug whereby
- a disconnect detect + dispose that occurs
- when the QueuePool has threads waiting
- for connections would leave those
- threads waiting for the duration of
- the timeout on the old pool (or indefinitely
- if timeout was disabled). The fix
- now notifies those waiters with a special
- exception case and has them move onto
- the new pool.
-
- .. change::
- :tags: engine, feature
- :tickets: 2516
-
- Dramatic improvement in memory
- usage of the event system; instance-level
- collections are no longer created for a
- particular type of event until
- instance-level listeners are established
- for that event.
-
- .. change::
- :tags: engine, bug
- :tickets: 2529
-
- Added gaerdbms import to mysql/__init__.py,
- the absence of which was preventing the new
- GAE dialect from being loaded.
-
- .. change::
- :tags: engine, bug
- :tickets: 2553
-
- Fixed cextension bug whereby the
- "ambiguous column error" would fail to
- function properly if the given index were
- a Column object and not a string.
- Note there are still some column-targeting
- issues here which are fixed in 0.8.
-
- .. change::
- :tags: engine, bug
- :tickets:
-
- Fixed the repr() of Enum to include
- the "name" and "native_enum" flags. Helps
- Alembic autogenerate.
-
- .. change::
- :tags: sqlite, bug
- :tickets: 2568
-
- Adjusted a very old bugfix which attempted
- to work around a SQLite issue that itself was
- "fixed" as of sqlite 3.6.14, regarding quotes
- surrounding a table name when using
- the "foreign_key_list" pragma. The fix has been
- adjusted to not interfere with quotes that
- are *actually in the name* of a column or table,
- to as much a degree as possible; sqlite still
- doesn't return the correct result for foreign_key_list()
- if the target table actually has quotes surrounding
- its name, as *part* of its name (i.e. """mytable""").
-
- .. change::
- :tags: sqlite, bug
- :tickets: 2265
-
- Adjusted column default reflection code to
- convert non-string values to string, to accommodate
- old SQLite versions that don't deliver
- default info as a string.
-
- .. change::
- :tags: sqlite, feature
- :tickets:
-
- Added support for the localtimestamp()
- SQL function implemented in SQLite, courtesy
- Richard Mitchell.
-
- .. change::
- :tags: postgresql, bug
- :tickets: 2531
-
- Columns in reflected primary key constraint
- are now returned in the order in which the constraint
- itself defines them, rather than how the table
- orders them. Courtesy Gunnlaugur Þór Briem..
-
- .. change::
- :tags: postgresql, bug
- :tickets: 2570
-
- Added 'terminating connection' to the list
- of messages we use to detect a disconnect with PG, which
- appears to be present in some versions when the server
- is restarted.
-
- .. change::
- :tags: bug, mysql
- :tickets:
-
- Updated mysqlconnector interface to use
- updated "client flag" and "charset" APIs,
- courtesy David McNelis.
-
- .. change::
- :tags: mssql, bug
- :tickets: 2538
-
- Fixed compiler bug whereby using a correlated
- subquery within an ORDER BY would fail to render correctly
- if the statement also used LIMIT/OFFSET, due to mis-rendering
- within the ROW_NUMBER() OVER clause. Fix courtesy
- sayap
-
- .. change::
- :tags: mssql, bug
- :tickets: 2545
-
- Fixed compiler bug whereby a given
- select() would be modified if it had an "offset"
- attribute, causing the construct to not compile
- correctly a second time.
-
- .. change::
- :tags: mssql, bug
- :tickets:
-
- Fixed bug where reflection of primary key constraint
- would double up columns if the same constraint/table
- existed in multiple schemas.
-
-.. changelog::
- :version: 0.7.8
- :released: Sat Jun 16 2012
-
- .. change::
- :tags: orm, bug
- :tickets: 2480
-
- Fixed bug whereby subqueryload() from
- a polymorphic mapping to a target would incur
- a new invocation of the query for each
- distinct class encountered in the polymorphic
- result.
-
- .. change::
- :tags: orm, bug
- :tickets: 2491, 1892
-
- Fixed bug in declarative
- whereby the precedence of columns
- in a joined-table, composite
- column (typically for id) would fail to
- be correct if the columns contained
- names distinct from their attribute
- names. This would cause things like
- primaryjoin conditions made against the
- entity attributes to be incorrect. Related
- to as this was supposed
- to be part of that, this is.
-
- .. change::
- :tags: orm, feature
- :tickets:
-
- The 'objects' argument to
- flush() is no longer deprecated, as some
- valid use cases have been identified.
-
- .. change::
- :tags: orm, bug
- :tickets: 2508
-
- Fixed identity_key() function which
- was not accepting a scalar argument
- for the identity. .
-
- .. change::
- :tags: orm, bug
- :tickets: 2497
-
- Fixed bug whereby populate_existing
- option would not propagate to subquery
- eager loaders. .
-
- .. change::
- :tags: bug, sql
- :tickets: 2499
-
- added BIGINT to types.__all__,
- BIGINT, BINARY, VARBINARY to sqlalchemy
- module namespace, plus test to ensure
- this breakage doesn't occur again.
-
- .. change::
- :tags: bug, sql
- :tickets: 2490
-
- Repaired common table expression
- rendering to function correctly when the
- SELECT statement contains UNION or other
- compound expressions, courtesy btbuilder.
-
- .. change::
- :tags: bug, sql
- :tickets: 2482
-
- Fixed bug whereby append_column()
- wouldn't function correctly on a cloned
- select() construct, courtesy
- Gunnlaugur Þór Briem.
-
- .. change::
- :tags: engine, bug
- :tickets: 2489
-
- Fixed memory leak in C version of
- result proxy whereby DBAPIs which don't deliver
- pure Python tuples for result rows would
- fail to decrement refcounts correctly.
- The most prominently affected DBAPI
- is pyodbc.
-
- .. change::
- :tags: engine, bug
- :tickets: 2503
-
- Fixed bug affecting Py3K whereby
- string positional parameters passed to
- engine/connection execute() would fail to be
- interpreted correctly, due to __iter__
- being present on Py3K string..
-
- .. change::
- :tags: postgresql, bug
- :tickets: 2510
-
- removed unnecessary table clause when
- reflecting enums,. Courtesy
- Gunnlaugur Þór Briem.
-
- .. change::
- :tags: oracle, bug
- :tickets: 2483
-
- Added ROWID to oracle.*.
-
- .. change::
- :tags: feature, mysql
- :tickets: 2484
-
- Added a new dialect for Google App
- Engine. Courtesy Richie Foreman.
-
-.. changelog::
- :version: 0.7.7
- :released: Sat May 05 2012
-
- .. change::
- :tags: orm, bug
- :tickets: 2477
-
- Fixed issue in unit of work
- whereby setting a non-None self-referential
- many-to-one relationship to None
- would fail to persist the change if the
- former value was not already loaded..
-
- .. change::
- :tags: orm, feature
- :tickets: 2443
-
- Added prefix_with() method
- to Query, calls upon select().prefix_with()
- to allow placement of MySQL SELECT
- directives in statements. Courtesy
- Diana Clarke
-
- .. change::
- :tags: orm, bug
- :tickets: 2409
-
- Fixed bug in 0.7.6 introduced by whereby column_mapped_collection
- used against columns that were mapped as
- joins or other indirect selectables
- would fail to function.
-
- .. change::
- :tags: orm, feature
- :tickets:
-
- 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.
-
- .. change::
- :tags: orm, bug
- :tickets: 2449
-
- 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.
-
- .. change::
- :tags: orm, bug
- :tickets: 2453
-
- Fixed bug in expression annotation
- mechanics which could lead to incorrect
- rendering of SELECT statements with aliases
- and joins, particularly when using
- column_property().
-
- .. change::
- :tags: orm, bug
- :tickets: 2454
-
- Fixed bug which would prevent
- OrderingList from being pickleable. Courtesy Jeff Dairiki
-
- .. change::
- :tags: orm, bug
- :tickets:
-
- Fixed bug in relationship comparisons
- whereby calling unimplemented methods like
- SomeClass.somerelationship.like() would
- produce a recursion overflow, instead
- of NotImplementedError.
-
- .. change::
- :tags: bug, sql
- :tickets:
-
- 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.
-
- .. change::
- :tags: feature, sql
- :tickets:
-
- 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.
-
- .. change::
- :tags: bug, sql
- :tickets:
-
- If conn.begin() fails when calling
- "with engine.begin()", the newly acquired
- Connection is closed explicitly before
- propagating the exception onward normally.
-
- .. change::
- :tags: bug, sql
- :tickets: 2474
-
- Add BINARY, VARBINARY to types.__all__.
-
- .. change::
- :tags: mssql, feature
- :tickets:
-
- 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.
-
- .. change::
- :tags: mssql, bug
- :tickets:
-
- 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.
-
- .. change::
- :tags: mssql, bug
- :tickets: 2468
-
- 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.
-
- .. change::
- :tags: postgresql, feature
- :tickets: 2445
-
- 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
-
- .. change::
- :tags: postgresql, bug
- :tickets: 2473
-
- removed unnecessary table clause
- when reflecting domains.
-
- .. change::
- :tags: bug, mysql
- :tickets: 2460
-
- 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.
-
- .. change::
- :tags: bug, mysql
- :tickets:
-
- Fixed bug whereby get_view_names() for
- "information_schema" schema would fail
- to retrieve views marked as "SYSTEM VIEW".
- courtesy Matthew Turland.
-
- .. change::
- :tags: bug, mysql
- :tickets: 2467
-
- 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.
-
- .. change::
- :tags: sqlite, feature
- :tickets: 2475
-
- Added SQLite execution option
- "sqlite_raw_colnames=True", will bypass
- attempts to remove "." from column names
- returned by SQLite cursor.description.
-
- .. change::
- :tags: sqlite, bug
- :tickets: 2525
-
- When the primary key column of a Table
- is replaced, such as via extend_existing,
- the "auto increment" column used by insert()
- constructs is reset. Previously it would
- remain referring to the previous primary
- key column.
-
-.. changelog::
- :version: 0.7.6
- :released: Wed Mar 14 2012
-
- .. change::
- :tags: orm, bug
- :tickets: 2424
-
- Fixed event registration bug
- which would primarily show up as
- events not being registered with
- sessionmaker() instances created
- after the event was associated
- with the Session class.
-
- .. change::
- :tags: orm, bug
- :tickets: 2425
-
- Fixed bug whereby a primaryjoin
- condition with a "literal" in it would
- raise an error on compile with certain
- kinds of deeply nested expressions
- which also needed to render the same
- bound parameter name more than once.
-
- .. change::
- :tags: orm, feature
- :tickets:
-
- Added "no_autoflush" context
- manager to Session, used with with:
- will temporarily disable autoflush.
-
- .. change::
- :tags: orm, feature
- :tickets: 1859
-
- Added cte() method to Query,
- invokes common table expression support
- from the Core (see below).
-
- .. change::
- :tags: orm, bug
- :tickets: 2403
-
- Removed the check for number of
- rows affected when doing a multi-delete
- against mapped objects. If an ON DELETE
- CASCADE exists between two rows, we can't
- get an accurate rowcount from the DBAPI;
- this particular count is not supported
- on most DBAPIs in any case, MySQLdb
- is the notable case where it is.
-
- .. change::
- :tags: orm, bug
- :tickets: 2409
-
- Fixed bug whereby objects using
- attribute_mapped_collection or
- column_mapped_collection could not be
- pickled.
-
- .. change::
- :tags: orm, bug
- :tickets: 2406
-
- Fixed bug whereby MappedCollection
- would not get the appropriate collection
- instrumentation if it were only used
- in a custom subclass that used
- @collection.internally_instrumented.
-
- .. change::
- :tags: orm, bug
- :tickets: 2419
-
- Fixed bug whereby SQL adaption mechanics
- would fail in a very nested scenario involving
- joined-inheritance, joinedload(), limit(), and a
- derived function in the columns clause.
-
- .. change::
- :tags: orm, bug
- :tickets: 2417
-
- Fixed the repr() for CascadeOptions to
- include refresh-expire. Also reworked
- CascadeOptions to be a <frozenset>.
-
- .. change::
- :tags: orm, feature
- :tickets: 2400
-
- Added the ability to query for
- Table-bound column names when using
- query(sometable).filter_by(colname=value).
-
- .. change::
- :tags: orm, bug
- :tickets:
-
- Improved the "declarative reflection"
- example to support single-table inheritance,
- multiple calls to prepare(), tables that
- are present in alternate schemas,
- establishing only a subset of classes
- as reflected.
-
- .. change::
- :tags: orm, bug
- :tickets: 2390
-
- Scaled back the test applied within
- flush() to check for UPDATE against partially
- NULL PK within one table to only actually
- happen if there's really an UPDATE to occur.
-
- .. change::
- :tags: orm, bug
- :tickets: 2352
-
- Fixed bug whereby if a method name
- conflicted with a column name, a
- TypeError would be raised when the mapper
- tried to inspect the __get__() method
- on the method object.
-
- .. change::
- :tags: bug, sql
- :tickets: 2427
-
- Fixed memory leak in core which would
- occur when C extensions were used with
- particular types of result fetches,
- in particular when orm query.count()
- were called.
-
- .. change::
- :tags: bug, sql
- :tickets: 2398
-
- Fixed issue whereby attribute-based
- column access on a row would raise
- AttributeError with non-C version,
- NoSuchColumnError with C version. Now
- raises AttributeError in both cases.
-
- .. change::
- :tags: feature, sql
- :tickets: 1859
-
- Added support for SQL standard
- common table expressions (CTE), allowing
- SELECT objects as the CTE source (DML
- not yet supported). This is invoked via
- the cte() method on any select() construct.
-
- .. change::
- :tags: bug, sql
- :tickets: 2392
-
- Added support for using the .key
- of a Column as a string identifier in a
- result set row. The .key is currently
- listed as an "alternate" name for a column,
- and is superseded by the name of a column
- which has that key value as its regular name.
- For the next major release
- of SQLAlchemy we may reverse this precedence
- so that .key takes precedence, but this
- is not decided on yet.
-
- .. change::
- :tags: bug, sql
- :tickets: 2413
-
- A warning is emitted when a not-present
- column is stated in the values() clause
- of an insert() or update() construct.
- Will move to an exception in 0.8.
-
- .. change::
- :tags: bug, sql
- :tickets: 2396
-
- A significant change to how labeling
- is applied to columns in SELECT statements
- allows "truncated" labels, that is label names
- that are generated in Python which exceed
- the maximum identifier length (note this is
- configurable via label_length on create_engine()),
- to be properly referenced when rendered inside
- of a subquery, as well as to be present
- in a result set row using their original
- in-Python names.
-
- .. change::
- :tags: bug, sql
- :tickets: 2402
-
- Fixed bug in new "autoload_replace" flag
- which would fail to preserve the primary
- key constraint of the reflected table.
-
- .. change::
- :tags: bug, sql
- :tickets: 2380
-
- Index will raise when arguments passed
- cannot be interpreted as columns or expressions.
- Will warn when Index is created
- with no columns at all.
-
- .. change::
- :tags: engine, feature
- :tickets: 2407
-
- Added "no_parameters=True" execution
- option for connections. If no parameters
- are present, will pass the statement
- as cursor.execute(statement), thereby invoking
- the DBAPIs behavior when no parameter collection
- is present; for psycopg2 and mysql-python, this
- means not interpreting % signs in the string.
- This only occurs with this option, and not
- just if the param list is blank, as otherwise
- this would produce inconsistent behavior
- of SQL expressions that normally escape percent
- signs (and while compiling, can't know ahead of
- time if parameters will be present in
- some cases).
-
- .. change::
- :tags: engine, bug
- :tickets:
-
- Added execution_options() call to
- MockConnection (i.e., that used with
- strategy="mock") which acts as a pass through
- for arguments.
-
- .. change::
- :tags: engine, feature
- :tickets: 2378
-
- Added pool_reset_on_return argument
- to create_engine, allows control over
- "connection return" behavior. Also added
- new arguments 'rollback', 'commit', None
- to pool.reset_on_return to allow more control
- over connection return activity.
-
- .. change::
- :tags: engine, feature
- :tickets:
-
- Added some decent context managers
- to Engine, Connection::
-
- with engine.begin() as conn:
- # <work with conn in a transaction>
- ...
-
- and::
-
- with engine.connect() as conn:
- # <work with conn>
- ...
-
- Both close out the connection when done,
- commit or rollback transaction with errors
- on engine.begin().
-
- .. change::
- :tags: sqlite, bug
- :tickets: 2432
-
- Fixed bug in C extensions whereby
- string format would not be applied to a
- Numeric value returned as integer; this
- affected primarily SQLite which does
- not maintain numeric scale settings.
-
- .. change::
- :tags: mssql, feature
- :tickets: 2430
-
- Added support for MSSQL INSERT,
- UPDATE, and DELETE table hints, using
- new with_hint() method on UpdateBase.
-
- .. change::
- :tags: feature, mysql
- :tickets: 2386
-
- Added support for MySQL index and
- primary key constraint types
- (i.e. USING) via new mysql_using parameter
- to Index and PrimaryKeyConstraint,
- courtesy Diana Clarke.
-
- .. change::
- :tags: feature, mysql
- :tickets: 2394
-
- Added support for the "isolation_level"
- parameter to all MySQL dialects. Thanks
- to mu_mind for the patch here.
-
- .. change::
- :tags: oracle, feature
- :tickets: 2399
-
- Added a new create_engine() flag
- coerce_to_decimal=False, disables the precision
- numeric handling which can add lots of overhead
- by converting all numeric values to
- Decimal.
-
- .. change::
- :tags: oracle, bug
- :tickets: 2401
-
- Added missing compilation support for
- LONG
-
- .. change::
- :tags: oracle, bug
- :tickets: 2435
-
- Added 'LEVEL' to the list of reserved
- words for Oracle.
-
- .. change::
- :tags: examples, bug
- :tickets:
-
- Altered _params_from_query() function
- in Beaker example to pull bindparams from the
- fully compiled statement, as a quick means
- to get everything including subqueries in the
- columns clause, etc.
-
-.. changelog::
- :version: 0.7.5
- :released: Sat Jan 28 2012
-
- .. change::
- :tags: orm, bug
- :tickets: 2389
-
- Fixed issue where modified session state
- established after a failed flush would be committed
- as part of the subsequent transaction that
- begins automatically after manual call
- to rollback(). The state of the session is
- checked within rollback(), and if new state
- is present, a warning is emitted and
- restore_snapshot() is called a second time,
- discarding those changes.
-
- .. change::
- :tags: orm, bug
- :tickets: 2345
-
- Fixed regression from 0.7.4 whereby
- using an already instrumented column from a
- superclass as "polymorphic_on" failed to resolve
- the underlying Column.
-
- .. change::
- :tags: orm, bug
- :tickets: 2370
-
- Raise an exception if xyzload_all() is
- used inappropriately with two non-connected
- relationships.
-
- .. change::
- :tags: orm, feature
- :tickets:
-
- Added "class_registry" argument to
- declarative_base(). Allows two or more declarative
- bases to share the same registry of class names.
-
- .. change::
- :tags: orm, feature
- :tickets:
-
- query.filter() accepts multiple
- criteria which will join via AND, i.e.
- query.filter(x==y, z>q, ...)
-
- .. change::
- :tags: orm, feature
- :tickets: 2351
-
- Added new capability to relationship
- loader options to allow "default" loader strategies.
- Pass '*' to any of joinedload(), lazyload(),
- subqueryload(), or noload() and that becomes the
- loader strategy used for all relationships,
- except for those explicitly stated in the
- Query. Thanks to up-and-coming contributor
- Kent Bower for an exhaustive and well
- written test suite !
-
- .. change::
- :tags: orm, bug
- :tickets: 2367
-
- Fixed bug whereby event.listen(SomeClass)
- forced an entirely unnecessary compile of the
- mapper, making events very hard to set up
- at module import time (nobody noticed this ??)
-
- .. change::
- :tags: orm, bug
- :tickets:
-
- Fixed bug whereby hybrid_property didn't
- work as a kw arg in any(), has().
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed regression from 0.6 whereby if
- "load_on_pending" relationship() flag were used
- where a non-"get()" lazy clause needed to be
- emitted on a pending object, it would fail
- to load.
-
- .. change::
- :tags: orm, bug
- :tickets: 2371
-
- ensure pickleability of all ORM exceptions
- for multiprocessing compatibility.
-
- .. change::
- :tags: orm, bug
- :tickets: 2353
-
- implemented standard "can't set attribute" /
- "can't delete attribute" AttributeError when
- setattr/delattr used on a hybrid that doesn't
- define fset or fdel.
-
- .. change::
- :tags: orm, bug
- :tickets: 2362
-
- Fixed bug where unpickled object didn't
- have enough of its state set up to work
- correctly within the unpickle() event established
- by the mutable object extension, if the object
- needed ORM attribute access within
- __eq__() or similar.
-
- .. change::
- :tags: orm, bug
- :tickets: 2374
-
- Fixed bug where "merge" cascade could
- mis-interpret an unloaded attribute, if the
- load_on_pending flag were used with
- relationship(). Thanks to Kent Bower
- for tests.
-
- .. change::
- :tags: orm, feature
- :tickets: 2356
-
- New declarative reflection example
- added, illustrates how best to mix table reflection
- with declarative as well as uses some new features
- from.
-
- .. change::
- :tags: feature, sql
- :tickets: 2356
-
- New reflection feature "autoload_replace";
- when set to False on Table, the Table can be autoloaded
- without existing columns being replaced. Allows
- more flexible chains of Table construction/reflection
- to be constructed, including that it helps with
- combining Declarative with table reflection.
- See the new example on the wiki.
-
- .. change::
- :tags: bug, sql
- :tickets: 2356
-
- Improved the API for add_column() such that
- if the same column is added to its own table,
- an error is not raised and the constraints
- don't get doubled up. Also helps with some
- reflection/declarative patterns.
-
- .. change::
- :tags: feature, sql
- :tickets:
-
- Added "false()" and "true()" expression
- constructs to sqlalchemy.sql namespace, though
- not part of __all__ as of yet.
-
- .. change::
- :tags: feature, sql
- :tickets: 2361
-
- Dialect-specific compilers now raise
- CompileError for all type/statement compilation
- issues, instead of InvalidRequestError or ArgumentError.
- The DDL for CREATE TABLE will re-raise
- CompileError to include table/column information
- for the problematic column.
-
- .. change::
- :tags: bug, sql
- :tickets: 2381
-
- Fixed issue where the "required" exception
- would not be raised for bindparam() with required=True,
- if the statement were given no parameters at all.
-
- .. change::
- :tags: engine, bug
- :tickets: 2371
-
- Added __reduce__ to StatementError,
- DBAPIError, column errors so that exceptions
- are pickleable, as when using multiprocessing.
- However, not
- all DBAPIs support this yet, such as
- psycopg2.
-
- .. change::
- :tags: engine, bug
- :tickets: 2382
-
- Improved error messages when a non-string
- or invalid string is passed to any of the
- date/time processors used by SQLite, including
- C and Python versions.
-
- .. change::
- :tags: engine, bug
- :tickets: 2377
-
- Fixed bug whereby a table-bound Column
- object named "<a>_<b>" which matched a column
- labeled as "<tablename>_<colname>" could match
- inappropriately when targeting in a result
- set row.
-
- .. change::
- :tags: engine, bug
- :tickets: 2384
-
- Fixed bug in "mock" strategy whereby
- correct DDL visit method wasn't called, resulting
- in "CREATE/DROP SEQUENCE" statements being
- duplicated
-
- .. change::
- :tags: sqlite, bug
- :tickets: 2364
-
- the "name" of an FK constraint in SQLite
- is reflected as "None", not "0" or other
- integer value.
- SQLite does not appear to support constraint
- naming in any case.
-
- .. change::
- :tags: sqlite, bug
- :tickets: 2368
-
- sql.false() and sql.true() compile to
- 0 and 1, respectively in sqlite
-
- .. change::
- :tags: sqlite, bug
- :tickets:
-
- removed an erroneous "raise" in the
- SQLite dialect when getting table names
- and view names, where logic is in place
- to fall back to an older version of
- SQLite that doesn't have the
- "sqlite_temp_master" table.
-
- .. change::
- :tags: bug, mysql
- :tickets: 2376
-
- fixed regexp that filters out warnings
- for non-reflected "PARTITION" directives,
- thanks to George Reilly
-
- .. change::
- :tags: mssql, bug
- :tickets: 2340
-
- Adjusted the regexp used in the
- mssql.TIME type to ensure only six digits
- are received for the "microseconds" portion
- of the value, which is expected by
- Python's datetime.time(). Note that
- support for sending microseconds doesn't
- seem to be possible yet with pyodbc
- at least.
-
- .. change::
- :tags: mssql, bug
- :tickets: 2347
-
- Dropped the "30 char" limit on pymssql,
- based on reports that it's doing things
- better these days. pymssql hasn't been
- well tested and as the DBAPI is in flux
- it's still not clear what the status
- is on this driver and how SQLAlchemy's
- implementation should adapt.
-
- .. change::
- :tags: oracle, bug
- :tickets: 2388
-
- Added ORA-03135 to the never ending
- list of oracle "connection lost" errors
-
- .. change::
- :tags: core, bug
- :tickets: 2379
-
- Changed LRUCache, used by the mapper
- to cache INSERT/UPDATE/DELETE statements,
- to use an incrementing counter instead
- of a timestamp to track entries, for greater
- reliability versus using time.time(), which
- can cause test failures on some platforms.
-
- .. change::
- :tags: core, bug
- :tickets: 2383
-
- Added a boolean check for the "finalize"
- function within the pool connection proxy's
- weakref callback before calling it, so that a
- warning isn't emitted that this function is None
- when the application is exiting and gc has
- removed the function from the module before the
- weakref callback was invoked.
-
- .. change::
- :tags: bug, py3k
- :tickets: 2348
-
- Fixed inappropriate usage of util.py3k
- flag and renamed it to util.py3k_warning, since
- this flag is intended to detect the -3 flag
- series of import restrictions only.
-
- .. change::
- :tags: examples, feature
- :tickets: 2313
-
- Simplified the versioning example
- a bit to use a declarative mixin as well
- as an event listener, instead of a metaclass +
- SessionExtension.
-
- .. change::
- :tags: examples, bug
- :tickets: 2346
-
- Fixed large_collection.py to close the
- session before dropping tables.
-
-.. changelog::
- :version: 0.7.4
- :released: Fri Dec 09 2011
-
- .. change::
- :tags: orm, bug
- :tickets: 2315
-
- Fixed backref behavior when "popping" the
- value off of a many-to-one in response to
- a removal from a stale one-to-many - the operation
- is skipped, since the many-to-one has since
- been updated.
-
- .. change::
- :tags: orm, bug
- :tickets: 2264
-
- After some years of not doing this, added
- more granularity to the "is X a parent of Y"
- functionality, which is used when determining
- if the FK on "Y" needs to be "nulled out" as well
- as if "Y" should be deleted with delete-orphan
- cascade. The test now takes into account the
- Python identity of the parent as well its identity
- key, to see if the last known parent of Y is
- definitely X. If a decision
- can't be made, a StaleDataError is raised. The
- conditions where this error is raised are fairly
- rare, requiring that the previous parent was
- garbage collected, and previously
- could very well inappropriately update/delete
- a record that's since moved onto a new parent,
- though there may be some cases where
- "silent success" occurred previously that will now
- raise in the face of ambiguity.
- Expiring "Y" resets the "parent" tracker, meaning
- X.remove(Y) could then end up deleting Y even
- if X is stale, but this is the same behavior
- as before; it's advised to expire X also in that
- case.
-
- .. change::
- :tags: orm, bug
- :tickets: 2310
-
- fixed inappropriate evaluation of user-mapped
- object in a boolean context within query.get(). Also in 0.6.9.
-
- .. change::
- :tags: orm, bug
- :tickets: 2304
-
- Added missing comma to PASSIVE_RETURN_NEVER_SET
- symbol
-
- .. change::
- :tags: orm, bug
- :tickets: 1776
-
- Cls.column.collate("some collation") now
- works. Also in 0.6.9
-
- .. change::
- :tags: orm, bug
- :tickets: 2309
-
- the value of a composite attribute is now
- expired after an insert or update operation, instead
- of regenerated in place. This ensures that a
- column value which is expired within a flush
- will be loaded first, before the composite
- is regenerated using that value.
-
- .. change::
- :tags: orm, bug
- :tickets: 2309, 2308
-
- The fix in also emits the
- "refresh" event when the composite value is
- loaded on access, even if all column
- values were already present, as is appropriate.
- This fixes the "mutable" extension which relies
- upon the "load" event to ensure the _parents
- dictionary is up to date, fixes.
- Thanks to Scott Torborg for the test case here.
-
- .. change::
- :tags: orm, bug
- :tickets: 2312
-
- Fixed bug whereby a subclass of a subclass
- using concrete inheritance in conjunction with
- the new ConcreteBase or AbstractConcreteBase
- would fail to apply the subclasses deeper than
- one level to the "polymorphic loader" of each
- base
-
- .. change::
- :tags: orm, bug
- :tickets: 2312
-
- Fixed bug whereby a subclass of a subclass
- using the new AbstractConcreteBase would fail
- to acquire the correct "base_mapper" attribute
- when the "base" mapper was generated, thereby
- causing failures later on.
-
- .. change::
- :tags: orm, bug
- :tickets: 2316
-
- Fixed bug whereby column_property() created
- against ORM-level column could be treated as
- a distinct entity when producing certain
- kinds of joined-inh joins.
-
- .. change::
- :tags: orm, bug
- :tickets: 2297
-
- Fixed the error formatting raised when
- a tuple is inadvertently passed to session.query(). Also in 0.6.9.
-
- .. change::
- :tags: orm, bug
- :tickets: 2328
-
- Calls to query.join() to a single-table
- inheritance subclass are now tracked, and
- are used to eliminate the additional WHERE..
- IN criterion normally tacked on with single
- table inheritance, since the join should
- accommodate it. This allows OUTER JOIN
- to a single table subclass to produce
- the correct results, and overall will produce
- fewer WHERE criterion when dealing with
- single table inheritance joins.
-
- .. change::
- :tags: orm, bug
- :tickets: 2339
-
- __table_args__ can now be passed as
- an empty tuple as well as an empty dict.. Thanks to Fayaz Yusuf Khan
- for the patch.
-
- .. change::
- :tags: orm, bug
- :tickets: 2325
-
- Updated warning message when setting
- delete-orphan without delete to no longer
- refer to 0.6, as we never got around to
- upgrading this to an exception. Ideally
- this might be better as an exception but
- it's not critical either way.
-
- .. change::
- :tags: orm, feature
- :tickets: 2345, 2238
-
- polymorphic_on now accepts many
- new kinds of values:
-
- * standalone expressions that aren't
- otherwise mapped
- * column_property() objects
- * string names of any column_property()
- or attribute name of a mapped Column
-
- The docs include an example using
- the case() construct, which is likely to be
- a common constructed used here. and part of
-
- Standalone expressions in polymorphic_on
- propagate to single-table inheritance
- subclasses so that they are used in the
- WHERE /JOIN clause to limit rows to that
- subclass as is the usual behavior.
-
- .. change::
- :tags: orm, feature
- :tickets: 2301
-
- IdentitySet supports the - operator
- as the same as difference(), handy when dealing
- with Session.dirty etc.
-
- .. change::
- :tags: orm, feature
- :tickets:
-
- Added new value for Column autoincrement
- called "ignore_fk", can be used to force autoincrement
- on a column that's still part of a ForeignKeyConstraint.
- New example in the relationship docs illustrates
- its use.
-
- .. change::
- :tags: orm, bug
- :tickets:
-
- Fixed bug in get_history() when referring
- to a composite attribute that has no value;
- added coverage for get_history() regarding
- composites which is otherwise just a userland
- function.
-
- .. change::
- :tags: bug, sql
- :tickets: 2316, 2261
-
- related to, made some
- adjustments to the change from
- regarding the "from" list on a select(). The
- _froms collection is no longer memoized, as this
- simplifies various use cases and removes the
- need for a "warning" if a column is attached
- to a table after it was already used in an
- expression - the select() construct will now
- always produce the correct expression.
- There's probably no real-world
- performance hit here; select() objects are
- almost always made ad-hoc, and systems that
- wish to optimize the re-use of a select()
- would be using the "compiled_cache" feature.
- A hit which would occur when calling select.bind
- has been reduced, but the vast majority
- of users shouldn't be using "bound metadata"
- anyway :).
-
- .. change::
- :tags: feature, sql
- :tickets: 2166, 1944
-
- The update() construct can now accommodate
- multiple tables in the WHERE clause, which will
- render an "UPDATE..FROM" construct, recognized by
- PostgreSQL and MSSQL. When compiled on MySQL,
- will instead generate "UPDATE t1, t2, ..". MySQL
- additionally can render against multiple tables in the
- SET clause, if Column objects are used as keys
- in the "values" parameter or generative method.
-
- .. change::
- :tags: feature, sql
- :tickets: 77
-
- Added accessor to types called "python_type",
- returns the rudimentary Python type object
- for a particular TypeEngine instance, if known,
- else raises NotImplementedError.
-
- .. change::
- :tags: bug, sql
- :tickets: 2261, 2319
-
- further tweak to the fix from,
- so that generative methods work a bit better
- off of cloned (this is almost a non-use case though).
- In particular this allows with_only_columns()
- to behave more consistently. Added additional
- documentation to with_only_columns() to clarify
- expected behavior, which changed as a result
- of.
-
- .. change::
- :tags: engine, bug
- :tickets: 2317
-
- Fixed bug whereby transaction.rollback()
- would throw an error on an invalidated
- connection if the transaction were a
- two-phase or savepoint transaction.
- For plain transactions, rollback() is a no-op
- if the connection is invalidated, so while
- it wasn't 100% clear if it should be a no-op,
- at least now the interface is consistent.
-
- .. change::
- :tags: feature, schema
- :tickets:
-
- Added new support for remote "schemas":
-
- .. change::
- :tags: schema
- :tickets:
-
- MetaData() accepts "schema" and "quote_schema"
- arguments, which will be applied to the same-named
- arguments of a Table
- or Sequence which leaves these at their default
- of ``None``.
-
- .. change::
- :tags: schema
- :tickets:
-
- Sequence accepts "quote_schema" argument
-
- .. change::
- :tags: schema
- :tickets:
-
- tometadata() for Table will use the "schema"
- of the incoming MetaData for the new Table
- if the schema argument is explicitly "None"
-
- .. change::
- :tags: schema
- :tickets:
-
- Added CreateSchema and DropSchema DDL
- constructs - these accept just the string
- name of a schema and a "quote" flag.
-
- .. change::
- :tags: schema
- :tickets:
-
- When using default "schema" with MetaData,
- ForeignKey will also assume the "default" schema
- when locating remote table. This allows the "schema"
- argument on MetaData to be applied to any
- set of Table objects that otherwise don't have
- a "schema".
-
- .. change::
- :tags: schema
- :tickets: 1679
-
- a "has_schema" method has been implemented
- on dialect, but only works on PostgreSQL so far.
- Courtesy Manlio Perillo.
-
- .. change::
- :tags: feature, schema
- :tickets: 1410
-
- The "extend_existing" flag on Table
- now allows for the reflection process to take
- effect for a Table object that's already been
- defined; when autoload=True and extend_existing=True
- are both set, the full set of columns will be
- reflected from the Table which will then
- *overwrite* those columns already present,
- rather than no activity occurring. Columns that
- are present directly in the autoload run
- will be used as always, however.
-
- .. change::
- :tags: bug, schema
- :tickets:
-
- Fixed bug whereby TypeDecorator would
- return a stale value for _type_affinity, when
- using a TypeDecorator that "switches" types,
- like the CHAR/UUID type.
-
- .. change::
- :tags: bug, schema
- :tickets:
-
- Fixed bug whereby "order_by='foreign_key'"
- option to Inspector.get_table_names
- wasn't implementing the sort properly, replaced
- with the existing sort algorithm
-
- .. change::
- :tags: bug, schema
- :tickets: 2305
-
- the "name" of a column-level CHECK constraint,
- if present, is now rendered in the CREATE TABLE
- statement using "CONSTRAINT <name> CHECK <expression>".
-
- .. change::
- :tags: pyodbc, bug
- :tickets: 2318
-
- pyodbc-based dialects now parse the
- pyodbc accurately as far as observed
- pyodbc strings, including such gems
- as "py3-3.0.1-beta4"
-
- .. change::
- :tags: postgresql, bug
- :tickets: 2311
-
- PostgreSQL dialect memoizes that an ENUM of a
- particular name was processed
- during a create/drop sequence. This allows
- a create/drop sequence to work without any
- calls to "checkfirst", and also means with
- "checkfirst" turned on it only needs to
- check for the ENUM once.
-
- .. change::
- :tags: postgresql, feature
- :tickets:
-
- Added create_type constructor argument
- to pg.ENUM. When False, no CREATE/DROP or
- checking for the type will be performed as part
- of a table create/drop event; only the
- create()/drop)() methods called directly
- will do this. Helps with Alembic "offline"
- scripts.
-
- .. change::
- :tags: mssql, feature
- :tickets: 822
-
- lifted the restriction on SAVEPOINT
- for SQL Server. All tests pass using it,
- it's not known if there are deeper issues
- however.
-
- .. change::
- :tags: mssql, bug
- :tickets: 2336
-
- repaired the with_hint() feature which
- wasn't implemented correctly on MSSQL -
- usually used for the "WITH (NOLOCK)" hint
- (which you shouldn't be using anyway !
- use snapshot isolation instead :) )
-
- .. change::
- :tags: mssql, bug
- :tickets: 2318
-
- use new pyodbc version detection for
- _need_decimal_fix option.
-
- .. change::
- :tags: mssql, bug
- :tickets: 2343
-
- don't cast "table name" as NVARCHAR
- on SQL Server 2000. Still mostly in the dark
- what incantations are needed to make PyODBC
- work fully with FreeTDS 0.91 here, however.
-
- .. change::
- :tags: mssql, bug
- :tickets: 2269
-
- Decode incoming values when retrieving
- list of index names and the names of columns
- within those indexes.
-
- .. change::
- :tags: bug, mysql
- :tickets:
-
- Unicode adjustments allow latest pymysql
- (post 0.4) to pass 100% on Python 2.
-
- .. change::
- :tags: ext, feature
- :tickets:
-
- Added an example to the hybrid docs
- of a "transformer" - a hybrid that returns a
- query-transforming callable in combination
- with a custom comparator. Uses a new method
- on Query called with_transformation(). The use
- case here is fairly experimental, but only
- adds one line of code to Query.
-
- .. change::
- :tags: ext, bug
- :tickets:
-
- the @compiles decorator raises an
- informative error message when no "default"
- compilation handler is present, rather
- than KeyError.
-
- .. change::
- :tags: examples, bug
- :tickets:
-
- Fixed bug in history_meta.py example where
- the "unique" flag was not removed from a
- single-table-inheritance subclass which
- generates columns to put up onto the base.
-
-.. changelog::
- :version: 0.7.3
- :released: Sun Oct 16 2011
-
- .. change::
- :tags: general
- :tickets: 2279
-
- Adjusted the "importlater" mechanism, which is
- used internally to resolve import cycles,
- such that the usage of __import__ is completed
- when the import of sqlalchemy or sqlalchemy.orm
- is done, thereby avoiding any usage of __import__
- after the application starts new threads,
- fixes. Also in 0.6.9.
-
- .. change::
- :tags: orm
- :tickets: 2298
-
- Improved query.join() such that the "left" side
- can more flexibly be a non-ORM selectable,
- such as a subquery. A selectable placed
- in select_from() will now be used as the left
- side, favored over implicit usage
- of a mapped entity.
- If the join still fails based on lack of
- foreign keys, the error message includes
- this detail. Thanks to brianrhude
- on IRC for the test case.
-
- .. change::
- :tags: orm
- :tickets: 2241
-
- Added after_soft_rollback() Session event. This
- event fires unconditionally whenever rollback()
- is called, regardless of if an actual DBAPI
- level rollback occurred. This event
- is specifically designed to allow operations
- with the Session to proceed after a rollback
- when the Session.is_active is True.
-
- .. change::
- :tags: orm
- :tickets:
-
- added "adapt_on_names" boolean flag to orm.aliased()
- construct. Allows an aliased() construct
- to link the ORM entity to a selectable that contains
- aggregates or other derived forms of a particular
- attribute, provided the name is the same as that
- of the entity mapped column.
-
- .. change::
- :tags: orm
- :tickets:
-
- Added new flag expire_on_flush=False to column_property(),
- marks those properties that would otherwise be considered
- to be "readonly", i.e. derived from SQL expressions,
- to retain their value after a flush has occurred, including
- if the parent object itself was involved in an update.
-
- .. change::
- :tags: orm
- :tickets: 2237
-
- Enhanced the instrumentation in the ORM to support
- Py3K's new argument style of "required kw arguments",
- i.e. fn(a, b, \*, c, d), fn(a, b, \*args, c, d).
- Argument signatures of mapped object's __init__
- method will be preserved, including required kw rules.
-
- .. change::
- :tags: orm
- :tickets: 2282
-
- Fixed bug in unit of work whereby detection of
- "cycles" among classes in highly interlinked patterns
- would not produce a deterministic
- result; thereby sometimes missing some nodes that
- should be considered cycles and causing further
- issues down the road. Note this bug is in 0.6
- also; not backported at the moment.
-
- .. change::
- :tags: orm
- :tickets:
-
- Fixed a variety of synonym()-related regressions
- from 0.6:
-
- * making a synonym against a synonym now works.
- * synonyms made against a relationship() can
- be passed to query.join(), options sent
- to query.options(), passed by name
- to query.with_parent().
-
- .. change::
- :tags: orm
- :tickets: 2287
-
- Fixed bug whereby mapper.order_by attribute would
- be ignored in the "inner" query within a
- subquery eager load. .
- Also in 0.6.9.
-
- .. change::
- :tags: orm
- :tickets: 2267
-
- Identity map .discard() uses dict.pop(,None)
- internally instead of "del" to avoid KeyError/warning
- during a non-determinate gc teardown
-
- .. change::
- :tags: orm
- :tickets: 2253
-
- Fixed regression in new composite rewrite where
- deferred=True option failed due to missing
- import
-
- .. change::
- :tags: orm
- :tickets: 2248
-
- Reinstated "comparator_factory" argument to
- composite(), removed when 0.7 was released.
-
- .. change::
- :tags: orm
- :tickets: 2247
-
- Fixed bug in query.join() which would occur
- in a complex multiple-overlapping path scenario,
- where the same table could be joined to
- twice. Thanks *much* to Dave Vitek
- for the excellent fix here.
-
- .. change::
- :tags: orm
- :tickets:
-
- Query will convert an OFFSET of zero when
- slicing into None, so that needless OFFSET
- clauses are not invoked.
-
- .. change::
- :tags: orm
- :tickets:
-
- Repaired edge case where mapper would fail
- to fully update internal state when a relationship
- on a new mapper would establish a backref on the
- first mapper.
-
- .. change::
- :tags: orm
- :tickets: 2260
-
- Fixed bug whereby if __eq__() was
- redefined, a relationship many-to-one lazyload
- would hit the __eq__() and fail.
- Does not apply to 0.6.9.
-
- .. change::
- :tags: orm
- :tickets: 2196
-
- Calling class_mapper() and passing in an object
- that is not a "type" (i.e. a class that could
- potentially be mapped) now raises an informative
- ArgumentError, rather than UnmappedClassError.
-
- .. change::
- :tags: orm
- :tickets:
-
- New event hook, MapperEvents.after_configured().
- Called after a configure() step has completed and
- mappers were in fact affected. Theoretically this
- event is called once per application, unless new mappings
- are constructed after existing ones have been used
- already.
-
- .. change::
- :tags: orm
- :tickets: 2281
-
- When an open Session is garbage collected, the objects
- within it which remain are considered detached again
- when they are add()-ed to a new Session.
- This is accomplished by an extra check that the previous
- "session_key" doesn't actually exist among the pool
- of Sessions.
-
- .. change::
- :tags: orm
- :tickets: 2239
-
- New declarative features:
-
- * __declare_last__() method, establishes an event
- listener for the class method that will be called
- when mappers are completed with the final "configure"
- step.
- * __abstract__ flag. The class will not be mapped
- at all when this flag is present on the class.
- * New helper classes ConcreteBase, AbstractConcreteBase.
- Allow concrete mappings using declarative which automatically
- set up the "polymorphic_union" when the "configure"
- mapper step is invoked.
- * The mapper itself has semi-private methods that allow
- the "with_polymorphic" selectable to be assigned
- to the mapper after it has already been configured.
-
- .. change::
- :tags: orm
- :tickets: 2283
-
- Declarative will warn when a subclass' base uses
- @declared_attr for a regular column - this attribute
- does not propagate to subclasses.
-
- .. change::
- :tags: orm
- :tickets: 2280
-
- The integer "id" used to link a mapped instance with
- its owning Session is now generated by a sequence
- generation function rather than id(Session), to
- eliminate the possibility of recycled id() values
- causing an incorrect result, no need to check that
- object actually in the session.
-
- .. change::
- :tags: orm
- :tickets: 2257
-
- Behavioral improvement: empty
- conjunctions such as and_() and or_() will be
- flattened in the context of an enclosing conjunction,
- i.e. and_(x, or_()) will produce 'X' and not 'X AND
- ()'..
-
- .. change::
- :tags: orm
- :tickets: 2261
-
- Fixed bug regarding calculation of "from" list
- for a select() element. The "from" calc is now
- delayed, so that if the construct uses a Column
- object that is not yet attached to a Table,
- but is later associated with a Table, it generates
- SQL using the table as a FROM. This change
- impacted fairly deeply the mechanics of how
- the FROM list as well as the "correlates" collection
- is calculated, as some "clause adaption" schemes
- (these are used very heavily in the ORM)
- were relying upon the fact that the "froms"
- collection would typically be cached before the
- adaption completed. The rework allows it
- such that the "froms" collection can be cleared
- and re-generated at any time.
-
- .. change::
- :tags: orm
- :tickets: 2270
-
- Fixed bug whereby with_only_columns() method of
- Select would fail if a selectable were passed.. Also in 0.6.9.
-
- .. change::
- :tags: schema
- :tickets: 2284
-
- Modified Column.copy() to use _constructor(),
- which defaults to self.__class__, in order to
- create the new object. This allows easier support
- of subclassing Column.
-
- .. change::
- :tags: schema
- :tickets: 2223
-
- Added a slightly nicer __repr__() to SchemaItem
- classes. Note the repr here can't fully support
- the "repr is the constructor" idea since schema
- items can be very deeply nested/cyclical, have
- late initialization of some things, etc.
-
- .. change::
- :tags: engine
- :tickets: 2254
-
- The recreate() method in all pool classes uses
- self.__class__ to get at the type of pool
- to produce, in the case of subclassing. Note
- there's no usual need to subclass pools.
-
- .. change::
- :tags: engine
- :tickets: 2243
-
- Improvement to multi-param statement logging,
- long lists of bound parameter sets will be
- compressed with an informative indicator
- of the compression taking place. Exception
- messages use the same improved formatting.
-
- .. change::
- :tags: engine
- :tickets:
-
- Added optional "sa_pool_key" argument to
- pool.manage(dbapi).connect() so that serialization
- of args is not necessary.
-
- .. change::
- :tags: engine
- :tickets: 2286
-
- The entry point resolution supported by
- create_engine() now supports resolution of
- individual DBAPI drivers on top of a built-in
- or entry point-resolved dialect, using the
- standard '+' notation - it's converted to
- a '.' before being resolved as an entry
- point.
-
- .. change::
- :tags: engine
- :tickets: 2299
-
- Added an exception catch + warning for the
- "return unicode detection" step within connect,
- allows databases that crash on NVARCHAR to
- continue initializing, assuming no NVARCHAR
- type implemented.
-
- .. change::
- :tags: types
- :tickets: 2258
-
- Extra keyword arguments to the base Float
- type beyond "precision" and "asdecimal" are ignored;
- added a deprecation warning here and additional
- docs, related to
-
- .. change::
- :tags: sqlite
- :tickets:
-
- Ensured that the same ValueError is raised for
- illegal date/time/datetime string parsed from
- the database regardless of whether C
- extensions are in use or not.
-
- .. change::
- :tags: postgresql
- :tickets: 2290
-
- Added "postgresql_using" argument to Index(), produces
- USING clause to specify index implementation for
- PG. . Thanks to Ryan P. Kelly for
- the patch.
-
- .. change::
- :tags: postgresql
- :tickets: 1839
-
- Added client_encoding parameter to create_engine()
- when the postgresql+psycopg2 dialect is used;
- calls the psycopg2 set_client_encoding() method
- with the value upon connect.
-
- .. change::
- :tags: postgresql
- :tickets: 2291, 2141
-
- Fixed bug related to whereby the
- same modified index behavior in PG 9 affected
- primary key reflection on a renamed column.. Also in 0.6.9.
-
- .. change::
- :tags: postgresql
- :tickets: 2256
-
- Reflection functions for Table, Sequence no longer
- case insensitive. Names can be differ only in case
- and will be correctly distinguished.
-
- .. change::
- :tags: postgresql
- :tickets:
-
- Use an atomic counter as the "random number"
- source for server side cursor names;
- conflicts have been reported in rare cases.
-
- .. change::
- :tags: postgresql
- :tickets: 2249
-
- Narrowed the assumption made when reflecting
- a foreign-key referenced table with schema in
- the current search path; an explicit schema will
- be applied to the referenced table only if
- it actually matches that of the referencing table,
- which also has an explicit schema. Previously
- it was assumed that "current" schema was synonymous
- with the full search_path.
-
- .. change::
- :tags: mysql
- :tickets: 2225
-
- a CREATE TABLE will put the COLLATE option
- after CHARSET, which appears to be part of
- MySQL's arbitrary rules regarding if it will actually
- work or not. Also in 0.6.9.
-
- .. change::
- :tags: mysql
- :tickets: 2293
-
- Added mysql_length parameter to Index construct,
- specifies "length" for indexes.
-
- .. change::
- :tags: mssql
- :tickets: 2273
-
- Changes to attempt support of FreeTDS 0.91 with
- Pyodbc. This includes that string binds are sent as
- Python unicode objects when FreeTDS 0.91 is detected,
- and a CAST(? AS NVARCHAR) is used when we detect
- for a table. However, I'd continue
- to characterize Pyodbc + FreeTDS 0.91 behavior as
- pretty crappy, there are still many queries such
- as used in reflection which cause a core dump on
- Linux, and it is not really usable at all
- on OSX, MemoryErrors abound and just plain broken
- unicode support.
-
- .. change::
- :tags: mssql
- :tickets: 2277
-
- The behavior of =/!= when comparing a scalar select
- to a value will no longer produce IN/NOT IN as of 0.8;
- this behavior is a little too heavy handed (use ``in_()`` if
- you want to emit IN) and now emits a deprecation warning.
- To get the 0.8 behavior immediately and remove the warning,
- a compiler recipe is given at
- https://www.sqlalchemy.org/docs/07/dialects/mssql.html#scalar-select-comparisons
- to override the behavior of visit_binary().
-
- .. change::
- :tags: mssql
- :tickets: 2222
-
- "0" is accepted as an argument for limit() which
- will produce "TOP 0".
-
- .. change::
- :tags: oracle
- :tickets: 2272
-
- Fixed ReturningResultProxy for zxjdbc dialect.. Regression from 0.6.
-
- .. change::
- :tags: oracle
- :tickets: 2252
-
- The String type now generates VARCHAR2 on Oracle
- which is recommended as the default VARCHAR.
- Added an explicit VARCHAR2 and NVARCHAR2 to the Oracle
- dialect as well. Using NVARCHAR still generates
- "NVARCHAR2" - there is no "NVARCHAR" on Oracle -
- this remains a slight breakage of the "uppercase types
- always give exactly that" policy. VARCHAR still
- generates "VARCHAR", keeping with the policy. If
- Oracle were to ever define "VARCHAR" as something
- different as they claim (IMHO this will never happen),
- the type would be available.
-
- .. change::
- :tags: ext
- :tickets: 2262
-
- SQLSoup will not be included in version 0.8
- of SQLAlchemy; while useful, we would like to
- keep SQLAlchemy itself focused on one ORM
- usage paradigm. SQLSoup will hopefully
- soon be superseded by a third party
- project.
-
- .. change::
- :tags: ext
- :tickets: 2236
-
- Added local_attr, remote_attr, attr accessors
- to AssociationProxy, providing quick access
- to the proxied attributes at the class
- level.
-
- .. change::
- :tags: ext
- :tickets: 2275
-
- Changed the update() method on association proxy
- dictionary to use a duck typing approach, i.e.
- checks for "keys", to discern between update({})
- and update((a, b)). Previously, passing a
- dictionary that had tuples as keys would be misinterpreted
- as a sequence.
-
- .. change::
- :tags: examples
- :tickets: 2266
-
- Adjusted dictlike-polymorphic.py example
- to apply the CAST such that it works on
- PG, other databases.
- Also in 0.6.9.
-
-.. changelog::
- :version: 0.7.2
- :released: Sun Jul 31 2011
-
- .. change::
- :tags: orm
- :tickets: 2213
-
- Feature enhancement: joined and subquery
- loading will now traverse already-present related
- objects and collections in search of unpopulated
- attributes throughout the scope of the eager load
- being defined, so that the eager loading that is
- specified via mappings or query options
- unconditionally takes place for the full depth,
- populating whatever is not already populated.
- Previously, this traversal would stop if a related
- object or collection were already present leading
- to inconsistent behavior (though would save on
- loads/cycles for an already-loaded graph). For a
- subqueryload, this means that the additional
- SELECT statements emitted by subqueryload will
- invoke unconditionally, no matter how much of the
- existing graph is already present (hence the
- controversy). The previous behavior of "stopping"
- is still in effect when a query is the result of
- an attribute-initiated lazyload, as otherwise an
- "N+1" style of collection iteration can become
- needlessly expensive when the same related object
- is encountered repeatedly. There's also an
- as-yet-not-public generative Query method
- _with_invoke_all_eagers()
- which selects old/new behavior
-
- .. change::
- :tags: orm
- :tickets: 2195
-
- A rework of "replacement traversal" within
- the ORM as it alters selectables to be against
- aliases of things (i.e. clause adaption) includes
- a fix for multiply-nested any()/has() constructs
- against a joined table structure.
-
- .. change::
- :tags: orm
- :tickets: 2234
-
- Fixed bug where query.join() + aliased=True
- from a joined-inh structure to itself on
- relationship() with join condition on the child
- table would convert the lead entity into the
- joined one inappropriately.
- Also in 0.6.9.
-
- .. change::
- :tags: orm
- :tickets: 2205
-
- Fixed regression from 0.6 where Session.add()
- against an object which contained None in a
- collection would raise an internal exception.
- Reverted this to 0.6's behavior which is to
- accept the None but obviously nothing is
- persisted. Ideally, collections with None
- present or on append() should at least emit a
- warning, which is being considered for 0.8.
-
- .. change::
- :tags: orm
- :tickets: 2191
-
- Load of a deferred() attribute on an object
- where row can't be located raises
- ObjectDeletedError instead of failing later
- on; improved the message in ObjectDeletedError
- to include other conditions besides a simple
- "delete".
-
- .. change::
- :tags: orm
- :tickets: 2224
-
- Fixed regression from 0.6 where a get history
- operation on some relationship() based attributes
- would fail when a lazyload would emit; this could
- trigger within a flush() under certain conditions. Thanks to the user who submitted
- the great test for this.
-
- .. change::
- :tags: orm
- :tickets: 2228
-
- Fixed bug apparent only in Python 3 whereby
- sorting of persistent + pending objects during
- flush would produce an illegal comparison,
- if the persistent object primary key
- is not a single integer.
- Also in 0.6.9
-
- .. change::
- :tags: orm
- :tickets: 2197
-
- Fixed bug whereby the source clause
- used by query.join() would be inconsistent
- if against a column expression that combined
- multiple entities together.
- Also in 0.6.9
-
- .. change::
- :tags: orm
- :tickets: 2215
-
- Fixed bug whereby if a mapped class
- redefined __hash__() or __eq__() to something
- non-standard, which is a supported use case
- as SQLA should never consult these,
- the methods would be consulted if the class
- was part of a "composite" (i.e. non-single-entity)
- result set.
- Also in 0.6.9.
-
- .. change::
- :tags: orm
- :tickets: 2240
-
- Added public attribute ".validators" to
- Mapper, an immutable dictionary view of
- all attributes that have been decorated
- with the @validates decorator. courtesy Stefano Fontanelli
-
- .. change::
- :tags: orm
- :tickets: 2188
-
- Fixed subtle bug that caused SQL to blow
- up if: column_property() against subquery +
- joinedload + LIMIT + order by the column
- property() occurred. .
- Also in 0.6.9
-
- .. change::
- :tags: orm
- :tickets: 2207
-
- The join condition produced by with_parent
- as well as when using a "dynamic" relationship
- against a parent will generate unique
- bindparams, rather than incorrectly repeating
- the same bindparam. .
- Also in 0.6.9.
-
- .. change::
- :tags: orm
- :tickets:
-
- Added the same "columns-only" check to
- mapper.polymorphic_on as used when
- receiving user arguments to
- relationship.order_by, foreign_keys,
- remote_side, etc.
-
- .. change::
- :tags: orm
- :tickets: 2190
-
- Fixed bug whereby comparison of column
- expression to a Query() would not call
- as_scalar() on the underlying SELECT
- statement to produce a scalar subquery,
- in the way that occurs if you called
- it on Query().subquery().
-
- .. change::
- :tags: orm
- :tickets: 2194
-
- Fixed declarative bug where a class inheriting
- from a superclass of the same name would fail
- due to an unnecessary lookup of the name
- in the _decl_class_registry.
-
- .. change::
- :tags: orm
- :tickets: 2199
-
- Repaired the "no statement condition"
- assertion in Query which would attempt
- to raise if a generative method were called
- after from_statement() were called.. Also in 0.6.9.
-
- .. change::
- :tags: sql
- :tickets: 2188
-
- Fixed two subtle bugs involving column
- correspondence in a selectable,
- one with the same labeled subquery repeated, the other
- when the label has been "grouped" and
- loses itself. Affects.
-
- .. change::
- :tags: schema
- :tickets: 2187
-
- New feature: with_variant() method on
- all types. Produces an instance of Variant(),
- a special TypeDecorator which will select
- the usage of a different type based on the
- dialect in use.
-
- .. change::
- :tags: schema
- :tickets:
-
- Added an informative error message when
- ForeignKeyConstraint refers to a column name in
- the parent that is not found. Also in 0.6.9.
-
- .. change::
- :tags: schema
- :tickets: 2206
-
- Fixed bug whereby adaptation of old append_ddl_listener()
- function was passing unexpected \**kw through
- to the Table event. Table gets no kws, the MetaData
- event in 0.6 would get "tables=somecollection",
- this behavior is preserved.
-
- .. change::
- :tags: schema
- :tickets:
-
- Fixed bug where "autoincrement" detection on
- Table would fail if the type had no "affinity"
- value, in particular this would occur when using
- the UUID example on the site that uses TypeEngine
- as the "impl".
-
- .. change::
- :tags: schema
- :tickets: 2209
-
- Added an improved repr() to TypeEngine objects
- that will only display constructor args which
- are positional or kwargs that deviate
- from the default.
-
- .. change::
- :tags: engine
- :tickets:
-
- Context manager provided by Connection.begin()
- will issue rollback() if the commit() fails,
- not just if an exception occurs.
-
- .. change::
- :tags: engine
- :tickets: 1682
-
- Use urllib.parse_qsl() in Python 2.6 and above,
- no deprecation warning about cgi.parse_qsl()
-
- .. change::
- :tags: engine
- :tickets:
-
- Added mixin class sqlalchemy.ext.DontWrapMixin.
- User-defined exceptions of this type are never
- wrapped in StatementException when they
- occur in the context of a statement
- execution.
-
- .. change::
- :tags: engine
- :tickets:
-
- StatementException wrapping will display the
- original exception class in the message.
-
- .. change::
- :tags: engine
- :tickets: 2201
-
- Failures on connect which raise dbapi.Error
- will forward the error to dialect.is_disconnect()
- and set the "connection_invalidated" flag if
- the dialect knows this to be a potentially
- "retryable" condition. Only Oracle ORA-01033
- implemented for now.
-
- .. change::
- :tags: sqlite
- :tickets: 2189
-
- SQLite dialect no longer strips quotes
- off of reflected default value, allowing
- a round trip CREATE TABLE to work.
- This is consistent with other dialects
- that also maintain the exact form of
- the default.
-
- .. change::
- :tags: postgresql
- :tickets: 2198
-
- Added new "postgresql_ops" argument to
- Index, allows specification of PostgreSQL
- operator classes for indexed columns. Courtesy Filip Zyzniewski.
-
- .. change::
- :tags: mysql
- :tickets: 2186
-
- Fixed OurSQL dialect to use ansi-neutral
- quote symbol "'" for XA commands instead
- of '"'. . Also in 0.6.9.
-
- .. change::
- :tags: mssql
- :tickets:
-
- Adjusted the pyodbc dialect such that bound
- values are passed as bytes and not unicode
- if the "Easysoft" unix drivers are detected.
- This is the same behavior as occurs with
- FreeTDS. Easysoft appears to segfault
- if Python unicodes are passed under
- certain circumstances.
-
- .. change::
- :tags: oracle
- :tickets: 2200
-
- Added ORA-00028 to disconnect codes, use
- cx_oracle _Error.code to get at the code,. Also in 0.6.9.
-
- .. change::
- :tags: oracle
- :tickets: 2201
-
- Added ORA-01033 to disconnect codes, which
- can be caught during a connection
- event.
-
- .. change::
- :tags: oracle
- :tickets: 2220
-
- repaired the oracle.RAW type which did not
- generate the correct DDL.
- Also in 0.6.9.
-
- .. change::
- :tags: oracle
- :tickets: 2212
-
- added CURRENT to reserved word list. Also in 0.6.9.
-
- .. change::
- :tags: oracle
- :tickets:
-
- Fixed bug in the mutable extension whereby
- if the same type were used twice in one
- mapping, the attributes beyond the first
- would not get instrumented.
-
- .. change::
- :tags: oracle
- :tickets:
-
- Fixed bug in the mutable extension whereby
- if None or a non-corresponding type were set,
- an error would be raised. None is now accepted
- which assigns None to all attributes,
- illegal values raise ValueError.
-
- .. change::
- :tags: examples
- :tickets:
-
- Repaired the examples/versioning test runner
- to not rely upon SQLAlchemy test libs,
- nosetests must be run from within
- examples/versioning to get around setup.cfg
- breaking it.
-
- .. change::
- :tags: examples
- :tickets:
-
- Tweak to examples/versioning to pick the
- correct foreign key in a multi-level
- inheritance situation.
-
- .. change::
- :tags: examples
- :tickets:
-
- Fixed the attribute shard example to check
- for bind param callable correctly in 0.7
- style.
-
-.. changelog::
- :version: 0.7.1
- :released: Sun Jun 05 2011
-
- .. change::
- :tags: general
- :tickets: 2184
-
- Added a workaround for Python bug 7511 where
- failure of C extension build does not
- raise an appropriate exception on Windows 64
- bit + VC express
-
- .. change::
- :tags: orm
- :tickets: 1912
-
- "delete-orphan" cascade is now allowed on
- self-referential relationships - this since
- SQLA 0.7 no longer enforces "parent with no
- child" at the ORM level; this check is left
- up to foreign key nullability.
- Related to
-
- .. change::
- :tags: orm
- :tickets: 2180
-
- Repaired new "mutable" extension to propagate
- events to subclasses correctly; don't
- create multiple event listeners for
- subclasses either.
-
- .. change::
- :tags: orm
- :tickets: 2170
-
- Modify the text of the message which occurs
- when the "identity" key isn't detected on
- flush, to include the common cause that
- the Column isn't set up to detect
- auto-increment correctly;.
- Also in 0.6.8.
-
- .. change::
- :tags: orm
- :tickets: 2182
-
- Fixed bug where transaction-level "deleted"
- collection wouldn't be cleared of expunged
- states, raising an error if they later
- became transient.
- Also in 0.6.8.
-
- .. change::
- :tags: sql
- :tickets:
-
- Fixed bug whereby metadata.reflect(bind)
- would close a Connection passed as a
- bind argument. Regression from 0.6.
-
- .. change::
- :tags: sql
- :tickets:
-
- Streamlined the process by which a Select
- determines what's in its '.c' collection.
- Behaves identically, except that a
- raw ClauseList() passed to select([])
- (which is not a documented case anyway) will
- now be expanded into its individual column
- elements instead of being ignored.
-
- .. change::
- :tags: engine
- :tickets:
-
- Deprecate schema/SQL-oriented methods on
- Connection/Engine that were never well known
- and are redundant: reflecttable(), create(),
- drop(), text(), engine.func
-
- .. change::
- :tags: engine
- :tickets: 2178
-
- Adjusted the __contains__() method of
- a RowProxy result row such that no exception
- throw is generated internally;
- NoSuchColumnError() also will generate its
- message regardless of whether or not the column
- construct can be coerced to a string.. Also in 0.6.8.
-
- .. change::
- :tags: sqlite
- :tickets: 2173
-
- Accept None from cursor.fetchone() when
- "PRAGMA read_uncommitted" is called to determine
- current isolation mode at connect time and
- default to SERIALIZABLE; this to support SQLite
- versions pre-3.3.0 that did not have this
- feature.
-
- .. change::
- :tags: postgresql
- :tickets: 2175
-
- Some unit test fixes regarding numeric arrays,
- MATCH operator. A potential floating-point
- inaccuracy issue was fixed, and certain tests
- of the MATCH operator only execute within an
- EN-oriented locale for now. .
- Also in 0.6.8.
-
- .. change::
- :tags: mysql
- :tickets:
-
- Unit tests pass 100% on MySQL installed
- on windows.
-
- .. change::
- :tags: mysql
- :tickets: 2181
-
- Removed the "adjust casing" step that would
- fail when reflecting a table on MySQL
- on windows with a mixed case name. After some
- experimenting with a windows MySQL server, it's
- been determined that this step wasn't really
- helping the situation much; MySQL does not return
- FK names with proper casing on non-windows
- platforms either, and removing the step at
- least allows the reflection to act more like
- it does on other OSes. A warning here
- has been considered but its difficult to
- determine under what conditions such a warning
- can be raised, so punted on that for now -
- added some docs instead.
-
- .. change::
- :tags: mysql
- :tickets:
-
- supports_sane_rowcount will be set to False
- if using MySQLdb and the DBAPI doesn't provide
- the constants.CLIENT module.
-
-.. changelog::
- :version: 0.7.0
- :released: Fri May 20 2011
-
- .. change::
- :tags:
- :tickets:
-
- This section documents those changes from 0.7b4
- to 0.7.0. For an overview of what's new in
- SQLAlchemy 0.7, see
- https://docs.sqlalchemy.org/en/latest/changelog/migration_07.html
-
- .. change::
- :tags: orm
- :tickets: 2069
-
- Fixed regression introduced in 0.7b4 (!) whereby
- query.options(someoption("nonexistent name")) would
- fail to raise an error. Also added additional
- error catching for cases where the option would
- try to build off a column-based element, further
- fixed up some of the error messages tailored
- in
-
- .. change::
- :tags: orm
- :tickets: 2162
-
- query.count() emits "count(*)" instead of
- "count(1)".
-
- .. change::
- :tags: orm
- :tickets: 2155
-
- Fine tuning of Query clause adaptation when
- from_self(), union(), or other "select from
- myself" operation, such that plain SQL expression
- elements added to filter(), order_by() etc.
- which are present in the nested "from myself"
- query *will* be adapted in the same way an ORM
- expression element will, since these
- elements are otherwise not easily accessible.
-
- .. change::
- :tags: orm
- :tickets: 2149
-
- Fixed bug where determination of "self referential"
- relationship would fail with no workaround
- for joined-inh subclass related to itself,
- or joined-inh subclass related to a subclass
- of that with no cols in the sub-sub class
- in the join condition.
- Also in 0.6.8.
-
- .. change::
- :tags: orm
- :tickets: 2153
-
- mapper() will ignore non-configured foreign keys
- to unrelated tables when determining inherit
- condition between parent and child class,
- but will raise as usual for unresolved
- columns and table names regarding the inherited
- table. This is an enhanced generalization of
- behavior that was already applied to declarative
- previously. 0.6.8 has a more
- conservative version of this which doesn't
- fundamentally alter how join conditions
- are determined.
-
- .. change::
- :tags: orm
- :tickets: 2144
-
- It is an error to call query.get() when the
- given entity is not a single, full class
- entity or mapper (i.e. a column). This is
- a deprecation warning in 0.6.8.
-
- .. change::
- :tags: orm
- :tickets: 2148
-
- Fixed a potential KeyError which under some
- circumstances could occur with the identity
- map, part of
-
- .. change::
- :tags: orm
- :tickets:
-
- added Query.with_session() method, switches
- Query to use a different session.
-
- .. change::
- :tags: orm
- :tickets: 2131
-
- horizontal shard query should use execution
- options per connection as per
-
- .. change::
- :tags: orm
- :tickets: 2151
-
- a non_primary mapper will inherit the _identity_class
- of the primary mapper. This so that a non_primary
- established against a class that's normally in an
- inheritance mapping will produce results that are
- identity-map compatible with that of the primary
- mapper (also in 0.6.8)
-
- .. change::
- :tags: orm
- :tickets: 2163
-
- Fixed the error message emitted for "can't
- execute syncrule for destination column 'q';
- mapper 'X' does not map this column" to
- reference the correct mapper. .
- Also in 0.6.8.
-
- .. change::
- :tags: orm
- :tickets: 1502
-
- polymorphic_union() gets a "cast_nulls" option,
- disables the usage of CAST when it renders
- the labeled NULL columns.
-
- .. change::
- :tags: orm
- :tickets:
-
- polymorphic_union() renders the columns in their
- original table order, as according to the first
- table/selectable in the list of polymorphic
- unions in which they appear. (which is itself
- an unordered mapping unless you pass an OrderedDict).
-
- .. change::
- :tags: orm
- :tickets: 2171
-
- Fixed bug whereby mapper mapped to an anonymous
- alias would fail if logging were used, due to
- unescaped % sign in the alias name.
- Also in 0.6.8.
-
- .. change::
- :tags: sql
- :tickets: 2167
-
- Fixed bug whereby nesting a label of a select()
- with another label in it would produce incorrect
- exported columns. Among other things this would
- break an ORM column_property() mapping against
- another column_property(). .
- Also in 0.6.8
-
- .. change::
- :tags: sql
- :tickets:
-
- Changed the handling in determination of join
- conditions such that foreign key errors are
- only considered between the two given tables.
- That is, t1.join(t2) will report FK errors
- that involve 't1' or 't2', but anything
- involving 't3' will be skipped. This affects
- join(), as well as ORM relationship and
- inherit condition logic.
-
- .. change::
- :tags: sql
- :tickets:
-
- Some improvements to error handling inside
- of the execute procedure to ensure auto-close
- connections are really closed when very
- unusual DBAPI errors occur.
-
- .. change::
- :tags: sql
- :tickets:
-
- metadata.reflect() and reflection.Inspector()
- had some reliance on GC to close connections
- which were internally procured, fixed this.
-
- .. change::
- :tags: sql
- :tickets: 2140
-
- Added explicit check for when Column .name
- is assigned as blank string
-
- .. change::
- :tags: sql
- :tickets: 2147
-
- Fixed bug whereby if FetchedValue was passed
- to column server_onupdate, it would not
- have its parent "column" assigned, added
- test coverage for all column default assignment
- patterns. also in 0.6.8
-
- .. change::
- :tags: postgresql
- :tickets:
-
- Fixed the psycopg2_version parsing in the
- psycopg2 dialect.
-
- .. change::
- :tags: postgresql
- :tickets: 2141
-
- Fixed bug affecting PG 9 whereby index reflection
- would fail if against a column whose name
- had changed. . Also in 0.6.8.
-
- .. change::
- :tags: mssql
- :tickets: 2169
-
- Fixed bug in MSSQL dialect whereby the aliasing
- applied to a schema-qualified table would leak
- into enclosing select statements.
- Also in 0.6.8.
-
- .. change::
- :tags: documentation
- :tickets: 2152
-
- Removed the usage of the "collections.MutableMapping"
- abc from the ext.mutable docs as it was being used
- incorrectly and makes the example more difficult
- to understand in any case.
-
- .. change::
- :tags: examples
- :tickets:
-
- removed the ancient "polymorphic association"
- examples and replaced with an updated set of
- examples that use declarative mixins,
- "generic_associations". Each presents an alternative
- table layout.
-
- .. change::
- :tags: ext
- :tickets: 2143
-
- Fixed bugs in sqlalchemy.ext.mutable extension where
- `None` was not appropriately handled, replacement
- events were not appropriately handled.
-
-.. changelog::
- :version: 0.7.0b4
- :released: Sun Apr 17 2011
-
- .. change::
- :tags: general
- :tickets:
-
- Changes to the format of CHANGES, this file.
- The format changes have been applied to
- the 0.7 releases.
-
- .. change::
- :tags: general
- :tickets:
-
- The "-declarative" changes will now be listed
- directly under the "-orm" section, as these
- are closely related.
-
- .. change::
- :tags: general
- :tickets:
-
- The 0.5 series changes have been moved to
- the file CHANGES_PRE_06 which replaces
- CHANGES_PRE_05.
-
- .. change::
- :tags: general
- :tickets:
-
- The changelog for 0.6.7 and subsequent within
- the 0.6 series is now listed only in the
- CHANGES file within the 0.6 branch.
- In the 0.7 CHANGES file (i.e. this file), all the
- 0.6 changes are listed inline within the 0.7
- section in which they were also applied
- (since all 0.6 changes are in 0.7 as well).
- Changes that apply to an 0.6 version here
- are noted as are if any differences in
- implementation/behavior are present.
-
- .. change::
- :tags: orm
- :tickets: 2122
-
- Some fixes to "evaluate" and "fetch" evaluation
- when query.update(), query.delete() are called.
- The retrieval of records is done after autoflush
- in all cases, and before update/delete is
- emitted, guarding against unflushed data present
- as well as expired objects failing during
- the evaluation.
-
- .. change::
- :tags: orm
- :tickets: 2063
-
- Reworded the exception raised when a flush
- is attempted of a subclass that is not polymorphic
- against the supertype.
-
- .. change::
- :tags: orm
- :tickets:
-
- Still more wording adjustments when a query option
- can't find the target entity. Explain that the
- path must be from one of the root entities.
-
- .. change::
- :tags: orm
- :tickets: 2123
-
- Some fixes to the state handling regarding
- backrefs, typically when autoflush=False, where
- the back-referenced collection wouldn't
- properly handle add/removes with no net
- change. Thanks to Richard Murri for the
- test case + patch.
- (also in 0.6.7).
-
- .. change::
- :tags: orm
- :tickets: 2127
-
- Added checks inside the UOW to detect the unusual
- condition of being asked to UPDATE or DELETE
- on a primary key value that contains NULL
- in it.
-
- .. change::
- :tags: orm
- :tickets: 2127
-
- Some refinements to attribute history. More
- changes are pending possibly in 0.8, but
- for now history has been modified such that
- scalar history doesn't have a "side effect"
- of populating None for a non-present value.
- This allows a slightly better ability to
- distinguish between a None set and no actual
- change, affects as well.
-
- .. change::
- :tags: orm
- :tickets: 2130
-
- a "having" clause would be copied from the
- inside to the outside query if from_self()
- were used; in particular this would break
- an 0.7 style count() query.
- (also in 0.6.7)
-
- .. change::
- :tags: orm
- :tickets: 2131
-
- the Query.execution_options() method now passes
- those options to the Connection rather than
- the SELECT statement, so that all available
- options including isolation level and
- compiled cache may be used.
-
- .. change::
- :tags: sql
- :tickets: 2131
-
- The "compiled_cache" execution option now raises
- an error when passed to a SELECT statement
- rather than a Connection. Previously it was
- being ignored entirely. We may look into
- having this option work on a per-statement
- level at some point.
-
- .. change::
- :tags: sql
- :tickets:
-
- Restored the "catchall" constructor on the base
- TypeEngine class, with a deprecation warning.
- This so that code which does something like
- Integer(11) still succeeds.
-
- .. change::
- :tags: sql
- :tickets: 2104
-
- Fixed regression whereby MetaData() coming
- back from unpickling did not keep track of
- new things it keeps track of now, i.e.
- collection of Sequence objects, list
- of schema names.
-
- .. change::
- :tags: sql
- :tickets: 2116
-
- The limit/offset keywords to select() as well
- as the value passed to select.limit()/offset()
- will be coerced to integer.
- (also in 0.6.7)
-
- .. change::
- :tags: sql
- :tickets:
-
- fixed bug where "from" clause gathering from an
- over() clause would be an itertools.chain() and
- not a list, causing "can only concatenate list"
- TypeError when combined with other clauses.
-
- .. change::
- :tags: sql
- :tickets: 2134
-
- Fixed incorrect usage of "," in over() clause
- being placed between the "partition" and "order by"
- clauses.
-
- .. change::
- :tags: sql
- :tickets: 2105
-
- Before/after attach events for PrimaryKeyConstraint
- now function, tests added for before/after events
- on all constraint types.
-
- .. change::
- :tags: sql
- :tickets: 2117
-
- Added explicit true()/false() constructs to expression
- lib - coercion rules will intercept "False"/"True"
- into these constructs. In 0.6, the constructs were
- typically converted straight to string, which was
- no longer accepted in 0.7.
-
- .. change::
- :tags: engine
- :tickets: 2129
-
- The C extension is now enabled by default on CPython
- 2.x with a fallback to pure python if it fails to
- compile.
-
- .. change::
- :tags: schema
- :tickets: 2109
-
- The 'useexisting' flag on Table has been superseded
- by a new pair of flags 'keep_existing' and
- 'extend_existing'. 'extend_existing' is equivalent
- to 'useexisting' - the existing Table is returned,
- and additional constructor elements are added.
- With 'keep_existing', the existing Table is returned,
- but additional constructor elements are not added -
- these elements are only applied when the Table
- is newly created.
-
- .. change::
- :tags: types
- :tickets: 2081
-
- REAL has been added to the core types. Supported
- by PostgreSQL, SQL Server, MySQL, SQLite. Note
- that the SQL Server and MySQL versions, which
- add extra arguments, are also still available
- from those dialects.
-
- .. change::
- :tags: types
- :tickets: 2106
-
- Added @event.listens_for() decorator, given
- target + event name, applies the decorated
- function as a listener.
-
- .. change::
- :tags: pool
- :tickets: 2103
-
- AssertionPool now stores the traceback indicating
- where the currently checked out connection was
- acquired; this traceback is reported within
- the assertion raised upon a second concurrent
- checkout; courtesy Gunnlaugur Briem
-
- .. change::
- :tags: pool
- :tickets:
-
- The "pool.manage" feature doesn't use pickle
- anymore to hash the arguments for each pool.
-
- .. change::
- :tags: sqlite
- :tickets: 2115
-
- Fixed bug where reflection of foreign key
- created as "REFERENCES <tablename>" without
- col name would fail.
- (also in 0.6.7)
-
- .. change::
- :tags: postgresql
- :tickets:
-
- Psycopg2 for Python 3 is now supported.
-
- .. change::
- :tags: postgresql
- :tickets: 2132
-
- Fixed support for precision numerics when using
- pg8000.
-
- .. change::
- :tags: oracle
- :tickets: 2100
-
- Using column names that would require quotes
- for the column itself or for a name-generated
- bind parameter, such as names with special
- characters, underscores, non-ascii characters,
- now properly translate bind parameter keys when
- talking to cx_oracle. (Also
- in 0.6.7)
-
- .. change::
- :tags: oracle
- :tickets: 2116
-
- Oracle dialect adds use_binds_for_limits=False
- create_engine() flag, will render the LIMIT/OFFSET
- values inline instead of as binds, reported to
- modify the execution plan used by Oracle. (Also in 0.6.7)
-
- .. change::
- :tags: documentation
- :tickets: 2029
-
- Documented SQLite DATE/TIME/DATETIME types. (also in 0.6.7)
-
- .. change::
- :tags: documentation
- :tickets: 2118
-
- Fixed mutable extension docs to show the
- correct type-association methods.
-
-.. changelog::
- :version: 0.7.0b3
- :released: Sun Mar 20 2011
-
- .. change::
- :tags: general
- :tickets:
-
- Lots of fixes to unit tests when run under PyPy
- (courtesy Alex Gaynor).
-
- .. change::
- :tags: orm
- :tickets: 2093
-
- Changed the underlying approach to query.count().
- query.count() is now in all cases exactly:
-
- query.
- from_self(func.count(literal_column('1'))).
- scalar()
-
- That is, "select count(1) from (<full query>)".
- This produces a subquery in all cases, but
- vastly simplifies all the guessing count()
- tried to do previously, which would still
- fail in many scenarios particularly when
- joined table inheritance and other joins
- were involved. If the subquery produced
- for an otherwise very simple count is really
- an issue, use query(func.count()) as an
- optimization.
-
- .. change::
- :tags: orm
- :tickets: 2087
-
- some changes to the identity map regarding
- rare weakref callbacks during iterations.
- The mutex has been removed as it apparently
- can cause a reentrant (i.e. in one thread) deadlock,
- perhaps when gc collects objects at the point of
- iteration in order to gain more memory. It is hoped
- that "dictionary changed during iteration" will
- be exceedingly rare as iteration methods internally
- acquire the full list of objects in a single values()
- call. Note 0.6.7 has a more conservative fix here
- which still keeps the mutex in place.
-
- .. change::
- :tags: orm
- :tickets: 2082
-
- A tweak to the unit of work causes it to order
- the flush along relationship() dependencies even if
- the given objects don't have any inter-attribute
- references in memory, which was the behavior in
- 0.5 and earlier, so a flush of Parent/Child with
- only foreign key/primary key set will succeed.
- This while still maintaining 0.6 and above's not
- generating a ton of useless internal dependency
- structures within the flush that don't correspond
- to state actually within the current flush.
-
- .. change::
- :tags: orm
- :tickets: 2069
-
- Improvements to the error messages emitted when
- querying against column-only entities in conjunction
- with (typically incorrectly) using loader options,
- where the parent entity is not fully present.
-
- .. change::
- :tags: orm
- :tickets: 2098
-
- Fixed bug in query.options() whereby a path
- applied to a lazyload using string keys could
- overlap a same named attribute on the wrong
- entity. Note 0.6.7 has a more conservative fix
- to this.
-
- .. change::
- :tags: declarative
- :tickets: 2091
-
- Arguments in __mapper_args__ that aren't "hashable"
- aren't mistaken for always-hashable, possibly-column
- arguments. (also in 0.6.7)
-
- .. change::
- :tags: sql
- :tickets:
-
- Added a fully descriptive error message for the
- case where Column is subclassed and _make_proxy()
- fails to make a copy due to TypeError on the
- constructor. The method _constructor should
- be implemented in this case.
-
- .. change::
- :tags: sql
- :tickets: 2095
-
- Added new event "column_reflect" for Table objects.
- Receives the info dictionary about a Column before
- the object is generated within reflection, and allows
- modification to the dictionary for control over
- most aspects of the resulting Column including
- key, name, type, info dictionary.
-
- .. change::
- :tags: sql
- :tickets:
-
- To help with the "column_reflect" event being used
- with specific Table objects instead of all instances
- of Table, listeners can be added to a Table object
- inline with its construction using a new argument
- "listeners", a list of tuples of the form
- (<eventname>, <fn>), which are applied to the Table
- before the reflection process begins.
-
- .. change::
- :tags: sql
- :tickets: 2085
-
- Added new generic function "next_value()", accepts
- a Sequence object as its argument and renders the
- appropriate "next value" generation string on the
- target platform, if supported. Also provides
- ".next_value()" method on Sequence itself.
-
- .. change::
- :tags: sql
- :tickets: 2084
-
- func.next_value() or other SQL expression can
- be embedded directly into an insert() construct,
- and if implicit or explicit "returning" is used
- in conjunction with a primary key column,
- the newly generated value will be present in
- result.inserted_primary_key.
-
- .. change::
- :tags: sql
- :tickets: 2089
-
- Added accessors to ResultProxy "returns_rows",
- "is_insert" (also in 0.6.7)
-
- .. change::
- :tags: engine
- :tickets: 2097
-
- Fixed AssertionPool regression bug.
-
- .. change::
- :tags: engine
- :tickets: 2060
-
- Changed exception raised to ArgumentError when an
- invalid dialect is specified.
-
- .. change::
- :tags: postgresql
- :tickets: 2092
-
- Added RESERVED_WORDS for postgresql dialect.
- (also in 0.6.7)
-
- .. change::
- :tags: postgresql
- :tickets: 2073
-
- Fixed the BIT type to allow a "length" parameter, "varying"
- parameter. Reflection also fixed.
- (also in 0.6.7)
-
- .. change::
- :tags: mssql
- :tickets: 2071
-
- Rewrote the query used to get the definition of a view,
- typically when using the Inspector interface, to
- use sys.sql_modules instead of the information schema,
- thereby allowing views definitions longer than 4000
- characters to be fully returned.
- (also in 0.6.7)
-
- .. change::
- :tags: firebird
- :tickets: 2083
-
- The "implicit_returning" flag on create_engine() is
- honored if set to False. (also in 0.6.7)
-
- .. change::
- :tags: informix
- :tickets: 2092
-
- Added RESERVED_WORDS informix dialect.
- (also in 0.6.7)
-
- .. change::
- :tags: ext
- :tickets: 2090
-
- The horizontal_shard ShardedSession class accepts the common
- Session argument "query_cls" as a constructor argument,
- to enable further subclassing of ShardedQuery. (also in 0.6.7)
-
- .. change::
- :tags: examples
- :tickets:
-
- Updated the association, association proxy examples
- to use declarative, added a new example
- dict_of_sets_with_default.py, a "pushing the envelope"
- example of association proxy.
-
- .. change::
- :tags: examples
- :tickets: 2090
-
- The Beaker caching example allows a "query_cls" argument
- to the query_callable() function.
- (also in 0.6.7)
-
-.. changelog::
- :version: 0.7.0b2
- :released: Sat Feb 19 2011
-
- .. change::
- :tags: orm
- :tickets: 2053
-
- Fixed bug whereby Session.merge() would call the
- load() event with one too few arguments.
-
- .. change::
- :tags: orm
- :tickets: 2052
-
- Added logic which prevents the generation of
- events from a MapperExtension or SessionExtension
- from generating do-nothing events for all the methods
- not overridden.
-
- .. change::
- :tags: declarative
- :tickets: 2058
-
- Fixed regression whereby composite() with
- Column objects placed inline would fail
- to initialize. The Column objects can now
- be inline with the composite() or external
- and pulled in via name or object ref.
-
- .. change::
- :tags: declarative
- :tickets: 2061
-
- Fix error message referencing old @classproperty
- name to reference @declared_attr
- (also in 0.6.7)
-
- .. change::
- :tags: declarative
- :tickets: 1468
-
- the dictionary at the end of the __table_args__
- tuple is now optional.
-
- .. change::
- :tags: sql
- :tickets: 2059
-
- Renamed the EngineEvents event class to
- ConnectionEvents. As these classes are never
- accessed directly by end-user code, this strictly
- is a documentation change for end users. Also
- simplified how events get linked to engines
- and connections internally.
-
- .. change::
- :tags: sql
- :tickets: 2055
-
- The Sequence() construct, when passed a MetaData()
- object via its 'metadata' argument, will be
- included in CREATE/DROP statements within
- metadata.create_all() and metadata.drop_all(),
- including "checkfirst" logic.
-
- .. change::
- :tags: sql
- :tickets: 2064
-
- The Column.references() method now returns True
- if it has a foreign key referencing the
- given column exactly, not just its parent
- table.
-
- .. change::
- :tags: postgresql
- :tickets: 2065
-
- Fixed regression from 0.6 where SMALLINT and
- BIGINT types would both generate SERIAL
- on an integer PK column, instead of
- SMALLINT and BIGSERIAL
-
- .. change::
- :tags: ext
- :tickets: 2054
-
- Association proxy now has correct behavior for
- any(), has(), and contains() when proxying
- a many-to-one scalar attribute to a one-to-many
- collection (i.e. the reverse of the 'typical'
- association proxy use case)
-
- .. change::
- :tags: examples
- :tickets:
-
- Beaker example now takes into account 'limit'
- and 'offset', bind params within embedded
- FROM clauses (like when you use union() or
- from_self()) when generating a cache key.
-
-.. changelog::
- :version: 0.7.0b1
- :released: Sat Feb 12 2011
-
- .. change::
- :tags:
- :tickets:
-
- Detailed descriptions of each change below are
- described at:
- https://docs.sqlalchemy.org/en/latest/changelog/migration_07.html
-
- .. change::
- :tags: general
- :tickets: 1902
-
- New event system, supersedes all extensions, listeners,
- etc.
-
- .. change::
- :tags: general
- :tickets: 1926
-
- Logging enhancements
-
- .. change::
- :tags: general
- :tickets: 1949
-
- Setup no longer installs a Nose plugin
-
- .. change::
- :tags: general
- :tickets:
-
- The "sqlalchemy.exceptions" alias in sys.modules
- has been removed. Base SQLA exceptions are
- available via "from sqlalchemy import exc".
- The "exceptions" alias for "exc" remains in
- "sqlalchemy" for now, it's just not patched into
- sys.modules.
-
- .. change::
- :tags: orm
- :tickets: 1923
-
- More succinct form of query.join(target, onclause)
-
- .. change::
- :tags: orm
- :tickets: 1903
-
- Hybrid Attributes, implements/supersedes synonym()
-
- .. change::
- :tags: orm
- :tickets: 2008
-
- Rewrite of composites
-
- .. change::
- :tags: orm
- :tickets:
-
- Mutation Event Extension, supersedes "mutable=True"
-
- .. seealso::
-
- :ref:`07_migration_mutation_extension`
-
- .. change::
- :tags: orm
- :tickets: 1980
-
- PickleType and ARRAY mutability turned off by default
-
- .. change::
- :tags: orm
- :tickets: 1895
-
- Simplified polymorphic_on assignment
-
- .. change::
- :tags: orm
- :tickets: 1912
-
- Flushing of Orphans that have no parent is allowed
-
- .. change::
- :tags: orm
- :tickets: 2041
-
- Adjusted flush accounting step to occur before
- the commit in the case of autocommit=True. This allows
- autocommit=True to work appropriately with
- expire_on_commit=True, and also allows post-flush session
- hooks to operate in the same transactional context
- as when autocommit=False.
-
- .. change::
- :tags: orm
- :tickets: 1973
-
- Warnings generated when collection members, scalar referents
- not part of the flush
-
- .. change::
- :tags: orm
- :tickets: 1876
-
- Non-`Table`-derived constructs can be mapped
-
- .. change::
- :tags: orm
- :tickets: 1942
-
- Tuple label names in Query Improved
-
- .. change::
- :tags: orm
- :tickets: 1892
-
- Mapped column attributes reference the most specific
- column first
-
- .. change::
- :tags: orm
- :tickets: 1896
-
- Mapping to joins with two or more same-named columns
- requires explicit declaration
-
- .. change::
- :tags: orm
- :tickets: 1875
-
- Mapper requires that polymorphic_on column be present
- in the mapped selectable
-
- .. change::
- :tags: orm
- :tickets: 1966
-
- compile_mappers() renamed configure_mappers(), simplified
- configuration internals
-
- .. change::
- :tags: orm
- :tickets: 2018
-
- the aliased() function, if passed a SQL FromClause element
- (i.e. not a mapped class), will return element.alias()
- instead of raising an error on AliasedClass.
-
- .. change::
- :tags: orm
- :tickets: 2027
-
- Session.merge() will check the version id of the incoming
- state against that of the database, assuming the mapping
- uses version ids and incoming state has a version_id
- assigned, and raise StaleDataError if they don't
- match.
-
- .. change::
- :tags: orm
- :tickets: 1996
-
- Session.connection(), Session.execute() accept 'bind',
- to allow execute/connection operations to participate
- in the open transaction of an engine explicitly.
-
- .. change::
- :tags: orm
- :tickets:
-
- Query.join(), Query.outerjoin(), eagerload(),
- eagerload_all(), others no longer allow lists
- of attributes as arguments (i.e. option([x, y, z])
- form, deprecated since 0.5)
-
- .. change::
- :tags: orm
- :tickets:
-
- ScopedSession.mapper is removed (deprecated since 0.5).
-
- .. change::
- :tags: orm
- :tickets: 2031
-
- Horizontal shard query places 'shard_id' in
- context.attributes where it's accessible by the
- "load()" event.
-
- .. change::
- :tags: orm
- :tickets: 2032
-
- A single contains_eager() call across
- multiple entities will indicate all collections
- along that path should load, instead of requiring
- distinct contains_eager() calls for each endpoint
- (which was never correctly documented).
-
- .. change::
- :tags: orm
- :tickets:
-
- The "name" field used in orm.aliased() now renders
- in the resulting SQL statement.
-
- .. change::
- :tags: orm
- :tickets: 1473
-
- Session weak_instance_dict=False is deprecated.
-
- .. change::
- :tags: orm
- :tickets: 2046
-
- An exception is raised in the unusual case that an
- append or similar event on a collection occurs after
- the parent object has been dereferenced, which
- prevents the parent from being marked as "dirty"
- in the session. Was a warning in 0.6.6.
-
- .. change::
- :tags: orm
- :tickets: 1069
-
- Query.distinct() now accepts column expressions
- as \*args, interpreted by the PostgreSQL dialect
- as DISTINCT ON (<expr>).
-
- .. change::
- :tags: orm
- :tickets: 2049
-
- Additional tuning to "many-to-one" relationship
- loads during a flush(). A change in version 0.6.6
- ([ticket:2002]) required that more "unnecessary" m2o
- loads during a flush could occur. Extra loading modes have
- been added so that the SQL emitted in this
- specific use case is trimmed back, while still
- retrieving the information the flush needs in order
- to not miss anything.
-
- .. change::
- :tags: orm
- :tickets:
-
- the value of "passive" as passed to
- attributes.get_history() should be one of the
- constants defined in the attributes package. Sending
- True or False is deprecated.
-
- .. change::
- :tags: orm
- :tickets: 2030
-
- Added a `name` argument to `Query.subquery()`, to allow
- a fixed name to be assigned to the alias object. (also in 0.6.7)
-
- .. change::
- :tags: orm
- :tickets: 2019
-
- A warning is emitted when a joined-table inheriting mapper
- has no primary keys on the locally mapped table
- (but has pks on the superclass table).
- (also in 0.6.7)
-
- .. change::
- :tags: orm
- :tickets: 2038
-
- Fixed bug where "middle" class in a polymorphic hierarchy
- would have no 'polymorphic_on' column if it didn't also
- specify a 'polymorphic_identity', leading to strange
- errors upon refresh, wrong class loaded when querying
- from that target. Also emits the correct WHERE criterion
- when using single table inheritance.
- (also in 0.6.7)
-
- .. change::
- :tags: orm
- :tickets: 1995
-
- Fixed bug where a column with a SQL or server side default
- that was excluded from a mapping with include_properties
- or exclude_properties would result in UnmappedColumnError. (also in 0.6.7)
-
- .. change::
- :tags: orm
- :tickets: 2046
-
- A warning is emitted in the unusual case that an
- append or similar event on a collection occurs after
- the parent object has been dereferenced, which
- prevents the parent from being marked as "dirty"
- in the session. This will be an exception in 0.7. (also in 0.6.7)
-
- .. change::
- :tags: declarative
- :tickets: 2050
-
- Added an explicit check for the case that the name
- 'metadata' is used for a column attribute on a
- declarative class. (also in 0.6.7)
-
- .. change::
- :tags: sql
- :tickets: 1844
-
- Added over() function, method to FunctionElement
- classes, produces the _Over() construct which
- in turn generates "window functions", i.e.
- "<window function> OVER (PARTITION BY <partition by>,
- ORDER BY <order by>)".
-
- .. change::
- :tags: sql
- :tickets: 805
-
- LIMIT/OFFSET clauses now use bind parameters
-
- .. change::
- :tags: sql
- :tickets: 1069
-
- select.distinct() now accepts column expressions
- as \*args, interpreted by the PostgreSQL dialect
- as DISTINCT ON (<expr>). Note this was already
- available via passing a list to the `distinct`
- keyword argument to select().
-
- .. change::
- :tags: sql
- :tickets:
-
- select.prefix_with() accepts multiple expressions
- (i.e. \*expr), 'prefix' keyword argument to select()
- accepts a list or tuple.
-
- .. change::
- :tags: sql
- :tickets:
-
- Passing a string to the `distinct` keyword argument
- of `select()` for the purpose of emitting special
- MySQL keywords (DISTINCTROW etc.) is deprecated -
- use `prefix_with()` for this.
-
- .. change::
- :tags: sql
- :tickets: 2006, 2005
-
- TypeDecorator works with primary key columns
-
- .. change::
- :tags: sql
- :tickets: 1897
-
- DDL() constructs now escape percent signs
-
- .. change::
- :tags: sql
- :tickets: 1917, 1893
-
- Table.c / MetaData.tables refined a bit, don't allow direct
- mutation
-
- .. change::
- :tags: sql
- :tickets: 1950
-
- Callables passed to `bindparam()` don't get evaluated
-
- .. change::
- :tags: sql
- :tickets: 1870
-
- types.type_map is now private, types._type_map
-
- .. change::
- :tags: sql
- :tickets: 1982
-
- Non-public Pool methods underscored
-
- .. change::
- :tags: sql
- :tickets: 723
-
- Added NULLS FIRST and NULLS LAST support. It's implemented
- as an extension to the asc() and desc() operators, called
- nullsfirst() and nullslast().
-
- .. change::
- :tags: sql
- :tickets:
-
- The Index() construct can be created inline with a Table
- definition, using strings as column names, as an alternative
- to the creation of the index outside of the Table.
-
- .. change::
- :tags: sql
- :tickets: 2001
-
- execution_options() on Connection accepts
- "isolation_level" argument, sets transaction isolation
- level for that connection only until returned to the
- connection pool, for those backends which support it
- (SQLite, PostgreSQL)
-
- .. change::
- :tags: sql
- :tickets: 2005
-
- A TypeDecorator of Integer can be used with a primary key
- column, and the "autoincrement" feature of various dialects
- as well as the "sqlite_autoincrement" flag will honor
- the underlying database type as being Integer-based.
-
- .. change::
- :tags: sql
- :tickets: 2020, 2021
-
- Established consistency when server_default is present
- on an Integer PK column. SQLA doesn't pre-fetch these,
- nor do they come back in cursor.lastrowid (DBAPI).
- Ensured all backends consistently return None
- in result.inserted_primary_key for these. Regarding
- reflection for this case, reflection of an int PK col
- with a server_default sets the "autoincrement" flag to False,
- except in the case of a PG SERIAL col where we detected a
- sequence default.
-
- .. change::
- :tags: sql
- :tickets: 2006
-
- Result-row processors are applied to pre-executed SQL
- defaults, as well as cursor.lastrowid, when determining
- the contents of result.inserted_primary_key.
-
- .. change::
- :tags: sql
- :tickets:
-
- Bind parameters present in the "columns clause" of a select
- are now auto-labeled like other "anonymous" clauses,
- which among other things allows their "type" to be meaningful
- when the row is fetched, as in result row processors.
-
- .. change::
- :tags: sql
- :tickets:
-
- TypeDecorator is present in the "sqlalchemy" import space.
-
- .. change::
- :tags: sql
- :tickets: 2015
-
- Non-DBAPI errors which occur in the scope of an `execute()`
- call are now wrapped in sqlalchemy.exc.StatementError,
- and the text of the SQL statement and repr() of params
- is included. This makes it easier to identify statement
- executions which fail before the DBAPI becomes
- involved.
-
- .. change::
- :tags: sql
- :tickets: 2048
-
- The concept of associating a ".bind" directly with a
- ClauseElement has been explicitly moved to Executable,
- i.e. the mixin that describes ClauseElements which represent
- engine-executable constructs. This change is an improvement
- to internal organization and is unlikely to affect any
- real-world usage.
-
- .. change::
- :tags: sql
- :tickets: 2028
-
- Column.copy(), as used in table.tometadata(), copies the
- 'doc' attribute. (also in 0.6.7)
-
- .. change::
- :tags: sql
- :tickets: 2023
-
- Added some defs to the resultproxy.c extension so that
- the extension compiles and runs on Python 2.4. (also in 0.6.7)
-
- .. change::
- :tags: sql
- :tickets: 2042
-
- The compiler extension now supports overriding the default
- compilation of expression._BindParamClause including that
- the auto-generated binds within the VALUES/SET clause
- of an insert()/update() statement will also use the new
- compilation rules. (also in 0.6.7)
-
- .. change::
- :tags: sql
- :tickets: 1921
-
- SQLite dialect now uses `NullPool` for file-based databases
-
- .. change::
- :tags: sql
- :tickets: 2036
-
- The path given as the location of a sqlite database is now
- normalized via os.path.abspath(), so that directory changes
- within the process don't affect the ultimate location
- of a relative file path.
-
- .. change::
- :tags: postgresql
- :tickets: 1083
-
- When explicit sequence execution derives the name
- of the auto-generated sequence of a SERIAL column,
- which currently only occurs if implicit_returning=False,
- now accommodates if the table + column name is greater
- than 63 characters using the same logic PostgreSQL uses. (also in 0.6.7)
-
- .. change::
- :tags: postgresql
- :tickets: 2044
-
- Added an additional libpq message to the list of "disconnect"
- exceptions, "could not receive data from server" (also in 0.6.7)
-
- .. change::
- :tags: mssql
- :tickets: 1833
-
- the String/Unicode types, and their counterparts VARCHAR/
- NVARCHAR, emit "max" as the length when no length is
- specified, so that the default length, normally '1'
- as per SQL server documentation, is instead
- 'unbounded'. This also occurs for the VARBINARY type..
-
- This behavior makes these types more closely compatible
- with PostgreSQL's VARCHAR type which is similarly unbounded
- when no length is specified.
-
- .. change::
- :tags: mysql
- :tickets: 1991
-
- New DBAPI support for pymysql, a pure Python port
- of MySQL-python.
-
- .. change::
- :tags: mysql
- :tickets: 2047
-
- oursql dialect accepts the same "ssl" arguments in
- create_engine() as that of MySQLdb.
- (also in 0.6.7)
-
- .. change::
- :tags: firebird
- :tickets: 1885
-
- Some adjustments so that Interbase is supported as well.
- FB/Interbase version idents are parsed into a structure
- such as (8, 1, 1, 'interbase') or (2, 1, 588, 'firebird')
- so they can be distinguished.
+++ /dev/null
-=============
-0.8 Changelog
-=============
-
-.. changelog_imports::
-
- .. include:: changelog_07.rst
- :start-line: 5
-
-
-.. changelog::
- :version: 0.8.7
- :released: July 22, 2014
-
- .. change::
- :tags: bug, mssql
- :versions: 1.0.0b1, 0.9.7
-
- Added statement encoding to the "SET IDENTITY_INSERT"
- statements which operate when an explicit INSERT is being
- interjected into an IDENTITY column, to support non-ascii table
- identifiers on drivers such as pyodbc + unix + py2k that don't
- support unicode statements.
-
- .. change::
- :tags: bug, mssql
- :versions: 1.0.0b1, 0.9.7
- :tickets: 3091
-
- In the SQL Server pyodbc dialect, repaired the implementation
- for the ``description_encoding`` dialect parameter, which when
- not explicitly set was preventing cursor.description from
- being parsed correctly in the case of result sets that
- contained names in alternate encodings. This parameter
- shouldn't be needed going forward.
-
- .. change::
- :tags: bug, sql
- :versions: 1.0.0b1, 0.9.7
- :tickets: 3124
-
- Fixed bug in :class:`.Enum` and other :class:`.SchemaType`
- subclasses where direct association of the type with a
- :class:`_schema.MetaData` would lead to a hang when events
- (like create events) were emitted on the :class:`_schema.MetaData`.
-
- .. change::
- :tags: bug, sql
- :versions: 1.0.0b1, 0.9.7
- :tickets: 3102
-
- Fixed a bug within the custom operator plus :meth:`.TypeEngine.with_variant`
- system, whereby using a :class:`.TypeDecorator` in conjunction with
- variant would fail with an MRO error when a comparison operator was used.
-
- .. change::
- :tags: bug, mysql
- :versions: 1.0.0b1, 0.9.7
- :tickets: 3101
-
- MySQL error 2014 "commands out of sync" appears to be raised as a
- ProgrammingError, not OperationalError, in modern MySQL-Python versions;
- all MySQL error codes that are tested for "is disconnect" are now
- checked within OperationalError and ProgrammingError regardless.
-
- .. change::
- :tags: bug, mysql
- :versions: 1.0.0b1, 0.9.5
- :tickets: 3085
-
- Fixed bug where column names added to ``mysql_length`` parameter
- on an index needed to have the same quoting for quoted names in
- order to be recognized. The fix makes the quotes optional but
- also provides the old behavior for backwards compatibility with those
- using the workaround.
-
- .. change::
- :tags: bug, declarative
- :versions: 1.0.0b1, 0.9.5
- :tickets: 3062
-
- The ``__mapper_args__`` dictionary is copied from a declarative
- mixin or abstract class when accessed, so that modifications made
- to this dictionary by declarative itself won't conflict with that
- of other mappings. The dictionary is modified regarding the
- ``version_id_col`` and ``polymorphic_on`` arguments, replacing the
- column within with the one that is officially mapped to the local
- class/table.
-
- .. change::
- :tags: bug, sql
- :versions: 0.9.5, 1.0.0b1
- :tickets: 3044
-
- Fixed bug in INSERT..FROM SELECT construct where selecting from a
- UNION would wrap the union in an anonymous (e.g. unlabeled) subquery.
-
- .. change::
- :tags: bug, postgresql
- :versions: 0.9.5, 1.0.0b1
- :tickets: 3053
-
- Added the ``hashable=False`` flag to the PG :class:`.HSTORE` type, which
- is needed to allow the ORM to skip over trying to "hash" an ORM-mapped
- HSTORE column when requesting it in a mixed column/entity list.
- Patch courtesy Gunnlaugur Þór Briem.
-
- .. change::
- :tags: bug, orm
- :versions: 0.9.5, 1.0.0b1
- :tickets: 3055
-
- Fixed bug in subquery eager loading where a long chain of
- eager loads across a polymorphic-subclass boundary in conjunction
- with polymorphic loading would fail to locate the subclass-link in the
- chain, erroring out with a missing property name on an
- :class:`.AliasedClass`.
-
- .. change::
- :tags: bug, ext
- :versions: 0.9.5, 1.0.0b1
- :tickets: 3051, 3093
-
- Fixed bug in mutable extension where :class:`.MutableDict` did not
- report change events for the ``setdefault()`` dictionary operation.
-
- .. change::
- :tags: bug, ext
- :versions: 0.9.5, 1.0.0b1
- :tickets: 3093, 3051
-
- Fixed bug where :meth:`.MutableDict.setdefault` didn't return the
- existing or new value (this bug was not released in any 0.8 version).
- Pull request courtesy Thomas Hervé.
-
- .. change::
- :tags: bug, mysql
- :versions: 0.9.5, 1.0.0b1
-
- Added support for reflecting tables where an index includes
- KEY_BLOCK_SIZE using an equal sign. Pull request courtesy
- Sean McGivern.
-
- .. change::
- :tags: bug, orm
- :tickets: 3047
- :versions: 0.9.5, 1.0.0b1
-
- Fixed ORM bug where the :func:`.class_mapper` function would mask
- AttributeErrors or KeyErrors that should raise during mapper
- configuration due to user errors. The catch for attribute/keyerror
- has been made more specific to not include the configuration step.
-
- .. change::
- :tags: bug, sql
- :tickets: 3045
- :versions: 0.9.5, 1.0.0b1
-
- Fixed bug where :meth:`_schema.Table.update` and :meth:`_schema.Table.delete`
- would produce an empty WHERE clause when an empty :func:`.and_()`
- or :func:`.or_()` or other blank expression were applied. This is
- now consistent with that of :func:`_expression.select`.
-
- .. change::
- :tags: bug, postgresql
- :versions: 0.9.5, 1.0.0b1
-
- Added a new "disconnect" message "connection has been closed unexpectedly".
- This appears to be related to newer versions of SSL.
- Pull request courtesy Antti Haapala.
-
-.. changelog::
- :version: 0.8.6
- :released: March 28, 2014
-
- .. change::
- :tags: bug, orm
- :tickets: 3006
- :versions: 0.9.4
-
- Fixed ORM bug where changing the primary key of an object, then marking
- it for DELETE would fail to target the correct row for DELETE.
-
- .. change::
- :tags: feature, postgresql
- :versions: 0.9.4
-
- Enabled "sane multi-row count" checking for the psycopg2 DBAPI, as
- this seems to be supported as of psycopg2 2.0.9.
-
- .. change::
- :tags: bug, postgresql
- :tickets: 3000
- :versions: 0.9.4
-
- Fixed regression caused by release 0.8.5 / 0.9.3's compatibility
- enhancements where index reflection on PostgreSQL versions specific
- to only the 8.1, 8.2 series again
- broke, surrounding the ever problematic int2vector type. While
- int2vector supports array operations as of 8.1, apparently it only
- supports CAST to a varchar as of 8.3.
-
- .. change::
- :tags: bug, orm
- :tickets: 2995,
- :versions: 0.9.4
-
- Fixed regression from 0.8.3 as a result of :ticket:`2818`
- where :meth:`_query.Query.exists` wouldn't work on a query that only
- had a :meth:`_query.Query.select_from` entry but no other entities.
-
- .. change::
- :tags: bug, general
- :tickets: 2986
- :versions: 0.9.4
-
- Adjusted ``setup.py`` file to support the possible future
- removal of the ``setuptools.Feature`` extension from setuptools.
- If this keyword isn't present, the setup will still succeed
- with setuptools rather than falling back to distutils. C extension
- building can be disabled now also by setting the
- DISABLE_SQLALCHEMY_CEXT environment variable. This variable works
- whether or not setuptools is even available.
-
- .. change::
- :tags: bug, ext
- :versions: 0.9.4
- :tickets: 2997
-
- Fixed bug in mutable extension as well as
- :func:`.attributes.flag_modified` where the change event would not be
- propagated if the attribute had been reassigned to itself.
-
- .. change::
- :tags: bug, orm
- :versions: 0.9.4
-
- Improved an error message which would occur if a query() were made
- against a non-selectable, such as a :func:`_expression.literal_column`, and then
- an attempt was made to use :meth:`_query.Query.join` such that the "left"
- side would be determined as ``None`` and then fail. This condition
- is now detected explicitly.
-
- .. change::
- :tags: bug, sql
- :versions: 0.9.4
- :tickets: 2977
-
- Fixed bug in :func:`.tuple_` construct where the "type" of essentially
- the first SQL expression would be applied as the "comparison type"
- to a compared tuple value; this has the effect in some cases of an
- inappropriate "type coercion" occurring, such as when a tuple that
- has a mix of String and Binary values improperly coerces target
- values to Binary even though that's not what they are on the left
- side. :func:`.tuple_` now expects heterogeneous types within its
- list of values.
-
- .. change::
- :tags: orm, bug
- :versions: 0.9.4
- :tickets: 2975
-
- Removed stale names from ``sqlalchemy.orm.interfaces.__all__`` and
- refreshed with current names, so that an ``import *`` from this
- module again works.
-
-.. changelog::
- :version: 0.8.5
- :released: February 19, 2014
-
- .. change::
- :tags: postgresql, bug
- :versions: 0.9.3
- :tickets: 2936
-
- Added an additional message to psycopg2 disconnect detection,
- "could not send data to server", which complements the existing
- "could not receive data from server" and has been observed by users.
-
- .. change::
- :tags: postgresql, bug
- :versions: 0.9.3
-
- Support has been improved for PostgreSQL reflection behavior on very old
- (pre 8.1) versions of PostgreSQL, and potentially other PG engines
- such as Redshift (assuming Redshift reports the version as < 8.1).
- The query for "indexes" as well as "primary keys" relies upon inspecting
- a so-called "int2vector" datatype, which refuses to coerce to an array
- prior to 8.1 causing failures regarding the "ANY()" operator used
- in the query. Extensive googling has located the very hacky, but
- recommended-by-PG-core-developer query to use when PG version < 8.1
- is in use, so index and primary key constraint reflection now work
- on these versions.
-
-
- .. change::
- :tags: feature, mysql
- :versions: 0.9.3
- :tickets: 2941
-
- Added new MySQL-specific :class:`.mysql.DATETIME` which includes
- fractional seconds support; also added fractional seconds support
- to :class:`.mysql.TIMESTAMP`. DBAPI support is limited, though
- fractional seconds are known to be supported by MySQL Connector/Python.
- Patch courtesy Geert JM Vanderkelen.
-
- .. change::
- :tags: bug, mysql
- :versions: 0.9.3
- :tickets: 2966
-
- Added support for the ``PARTITION BY`` and ``PARTITIONS``
- MySQL table keywords, specified as ``mysql_partition_by='value'`` and
- ``mysql_partitions='value'`` to :class:`_schema.Table`. Pull request
- courtesy Marcus McCurdy.
-
- .. change::
- :tags: bug, sql
- :versions: 0.9.3
- :tickets: 2944
-
- Fixed bug where calling :meth:`_expression.Insert.values` with an empty list
- or tuple would raise an IndexError. It now produces an empty
- insert construct as would be the case with an empty dictionary.
-
- .. change::
- :tags: bug, engine, pool
- :versions: 0.9.3
- :tickets: 2880, 2964
-
- Fixed a critical regression caused by :ticket:`2880` where the newly
- concurrent ability to return connections from the pool means that the
- "first_connect" event is now no longer synchronized either, thus leading
- to dialect mis-configurations under even minimal concurrency situations.
-
- .. change::
- :tags: bug, sqlite
-
- Restored a change that was missed in the backport of unique
- constraint reflection to 0.8, where :class:`.UniqueConstraint`
- with SQLite would fail if reserved keywords were included in the
- names of columns. Pull request courtesy Roman Podolyaka.
-
- .. change::
- :tags: bug, postgresql
- :tickets: 2291
- :versions: 0.9.3
-
- Revised this very old issue where the PostgreSQL "get primary key"
- reflection query were updated to take into account primary key constraints
- that were renamed; the newer query fails on very old versions of
- PostgreSQL such as version 7, so the old query is restored in those cases
- when server_version_info < (8, 0) is detected.
-
- .. change::
- :tags: bug, sql
- :tickets: 2957
- :versions: 0.9.3
-
- Fixed bug where :meth:`.ColumnOperators.in_` would go into an endless
- loop if erroneously passed a column expression whose comparator
- included the ``__getitem__()`` method, such as a column that uses the
- :class:`_postgresql.ARRAY` type.
-
- .. change::
- :tags: bug, orm
- :tickets: 2951
- :versions: 0.9.3
-
- Fixed bug where :meth:`_query.Query.get` would fail to consistently
- raise the :class:`.InvalidRequestError` that invokes when called
- on a query with existing criterion, when the given identity is
- already present in the identity map.
-
- .. change::
- :tags: bug, mysql
- :tickets: 2933
- :versions: 0.9.3
-
- Fixed bug which prevented MySQLdb-based dialects (e.g.
- pymysql) from working in Py3K, where a check for "connection
- charset" would fail due to Py3K's more strict value comparison
- rules. The call in question wasn't taking the database
- version into account in any case as the server version was
- still None at that point, so the method overall has been
- simplified to rely upon connection.character_set_name().
-
- .. change::
- :tags: bug, mysql
- :versions: 0.9.2
-
- Some missing methods added to the cymysql dialect, including
- _get_server_version_info() and _detect_charset(). Pullreq
- courtesy Hajime Nakagami.
-
- .. change::
- :tags: bug, py3k
-
- Fixed Py3K bug where a missing import would cause "literal binary"
- mode to fail to import "util.binary_type" when rendering a bound
- parameter. 0.9 handles this differently. Pull request courtesy
- Andreas Zeidler.
-
- .. change::
- :tags: bug, orm
- :versions: 0.9.2
-
- Fixed error message when an iterator object is passed to
- :func:`.class_mapper` or similar, where the error would fail to
- render on string formatting. Pullreq courtesy Kyle Stark.
-
- .. change::
- :tags: bug, firebird
- :versions: 0.9.0
- :tickets: 2897
-
- The firebird dialect will quote identifiers which begin with an
- underscore. Courtesy Treeve Jelbert.
-
- .. change::
- :tags: bug, firebird
- :versions: 0.9.0
-
- Fixed bug in Firebird index reflection where the columns within the
- index were not sorted correctly; they are now sorted
- in order of RDB$FIELD_POSITION.
-
- .. change::
- :tags: bug, mssql, firebird
- :versions: 0.9.0
-
- The "asdecimal" flag used with the :class:`.Float` type will now
- work with Firebird as well as the mssql+pyodbc dialects; previously the
- decimal conversion was not occurring.
-
- .. change::
- :tags: bug, mssql, pymssql
- :versions: 0.9.0
-
- Added "Net-Lib error during Connection reset by peer" message
- to the list of messages checked for "disconnect" within the
- pymssql dialect. Courtesy John Anderson.
-
- .. change::
- :tags: bug, sql
- :versions: 0.9.0
- :tickets: 2896
-
- Fixed issue where a primary key column that has a Sequence on it,
- yet the column is not the "auto increment" column, either because
- it has a foreign key constraint or ``autoincrement=False`` set,
- would attempt to fire the Sequence on INSERT for backends that don't
- support sequences, when presented with an INSERT missing the primary
- key value. This would take place on non-sequence backends like
- SQLite, MySQL.
-
- .. change::
- :tags: bug, sql
- :versions: 0.9.0
- :tickets: 2895
-
- Fixed bug with :meth:`_expression.Insert.from_select` method where the order
- of the given names would not be taken into account when generating
- the INSERT statement, thus producing a mismatch versus the column
- names in the given SELECT statement. Also noted that
- :meth:`_expression.Insert.from_select` implies that Python-side insert defaults
- cannot be used, since the statement has no VALUES clause.
-
- .. change::
- :tags: enhancement, sql
- :versions: 0.9.0
-
- The exception raised when a :class:`.BindParameter` is present
- in a compiled statement without a value now includes the key name
- of the bound parameter in the error message.
-
- .. change::
- :tags: bug, orm
- :versions: 0.9.0
- :tickets: 2887
-
- An adjustment to the :func:`.subqueryload` strategy which ensures that
- the query runs after the loading process has begun; this is so that
- the subqueryload takes precedence over other loaders that may be
- hitting the same attribute due to other eager/noload situations
- at the wrong time.
-
- .. change::
- :tags: bug, orm
- :versions: 0.9.0
- :tickets: 2885
-
- Fixed bug when using joined table inheritance from a table to a
- select/alias on the base, where the PK columns were also not same
- named; the persistence system would fail to copy primary key values
- from the base table to the inherited table upon INSERT.
-
- .. change::
- :tags: bug, orm
- :versions: 0.9.0
- :tickets: 2889
-
- :func:`.composite` will raise an informative error message when the
- columns/attribute (names) passed don't resolve to a Column or mapped
- attribute (such as an erroneous tuple); previously raised an unbound
- local.
-
- .. change::
- :tags: bug, declarative
- :versions: 0.9.0
- :tickets: 2888
-
- Error message when a string arg sent to :func:`_orm.relationship` which
- doesn't resolve to a class or mapper has been corrected to work
- the same way as when a non-string arg is received, which indicates
- the name of the relationship which had the configurational error.
-
-.. changelog::
- :version: 0.8.4
- :released: December 8, 2013
-
- .. change::
- :tags: bug, engine
- :versions: 0.9.0
- :tickets: 2881
-
- A DBAPI that raises an error on ``connect()`` which is not a subclass
- of dbapi.Error (such as ``TypeError``, ``NotImplementedError``, etc.)
- will propagate the exception unchanged. Previously,
- the error handling specific to the ``connect()`` routine would both
- inappropriately run the exception through the dialect's
- :meth:`.Dialect.is_disconnect` routine as well as wrap it in
- a :class:`sqlalchemy.exc.DBAPIError`. It is now propagated unchanged
- in the same way as occurs within the execute process.
-
- .. change::
- :tags: bug, engine, pool
- :versions: 0.9.0
- :tickets: 2880
-
- The :class:`.QueuePool` has been enhanced to not block new connection
- attempts when an existing connection attempt is blocking. Previously,
- the production of new connections was serialized within the block
- that monitored overflow; the overflow counter is now altered within
- its own critical section outside of the connection process itself.
-
- .. change::
- :tags: bug, engine, pool
- :versions: 0.9.0
- :tickets: 2522
-
- Made a slight adjustment to the logic which waits for a pooled
- connection to be available, such that for a connection pool
- with no timeout specified, it will every half a second break out of
- the wait to check for the so-called "abort" flag, which allows the
- waiter to break out in case the whole connection pool was dumped;
- normally the waiter should break out due to a notify_all() but it's
- possible this notify_all() is missed in very slim cases.
- This is an extension of logic first introduced in 0.8.0, and the
- issue has only been observed occasionally in stress tests.
-
- .. change::
- :tags: bug, mssql
- :versions: 0.9.0
-
- Fixed bug introduced in 0.8.0 where the ``DROP INDEX``
- statement for an index in MSSQL would render incorrectly if the
- index were in an alternate schema; the schemaname/tablename
- would be reversed. The format has been also been revised to
- match current MSSQL documentation. Courtesy Derek Harland.
-
- .. change::
- :tags: feature, sql
- :tickets: 1443
- :versions: 0.9.0b1
-
- Added support for "unique constraint" reflection, via the
- :meth:`_reflection.Inspector.get_unique_constraints` method.
- Thanks for Roman Podolyaka for the patch.
-
- .. change::
- :tags: bug, oracle
- :tickets: 2864
- :versions: 0.9.0
-
- Added ORA-02396 "maximum idle time" error code to list of
- "is disconnect" codes with cx_oracle.
-
- .. change::
- :tags: bug, engine
- :tickets: 2871
- :versions: 0.9.0
-
- Fixed bug where SQL statement would be improperly ASCII-encoded
- when a pre-DBAPI :class:`.StatementError` were raised within
- :meth:`_engine.Connection.execute`, causing encoding errors for
- non-ASCII statements. The stringification now remains within
- Python unicode thus avoiding encoding errors.
-
- .. change::
- :tags: bug, oracle
- :tickets: 2870
- :versions: 0.9.0
-
- Fixed bug where Oracle ``VARCHAR`` types given with no length
- (e.g. for a ``CAST`` or similar) would incorrectly render ``None CHAR``
- or similar.
-
- .. change::
- :tags: bug, ext
- :tickets: 2869
- :versions: 0.9.0
-
- Fixed bug which prevented the ``serializer`` extension from working
- correctly with table or column names that contain non-ASCII
- characters.
-
- .. change::
- :tags: bug, orm
- :tickets: 2818
- :versions: 0.9.0
-
- Fixed a regression introduced by :ticket:`2818` where the EXISTS
- query being generated would produce a "columns being replaced"
- warning for a statement with two same-named columns,
- as the internal SELECT wouldn't have use_labels set.
-
- .. change::
- :tags: bug, postgresql
- :tickets: 2855
- :versions: 0.9.0
-
- Fixed bug where index reflection would mis-interpret indkey values
- when using the pypostgresql adapter, which returns these values
- as lists vs. psycopg2's return type of string.
-
-.. changelog::
- :version: 0.8.3
- :released: October 26, 2013
-
- .. change::
- :tags: bug, oracle
- :tickets: 2853
- :versions: 0.9.0b1
-
- Fixed bug where Oracle table reflection using synonyms would fail
- if the synonym and the table were in different remote schemas.
- Patch to fix courtesy Kyle Derr.
-
- .. change::
- :tags: bug, sql
- :tickets: 2849
- :versions: 0.9.0b1
-
- Fixed bug where :func:`.type_coerce` would not interpret ORM
- elements with a ``__clause_element__()`` method properly.
-
- .. change::
- :tags: bug, sql
- :tickets: 2842
- :versions: 0.9.0b1
-
- The :class:`.Enum` and :class:`.Boolean` types now bypass
- any custom (e.g. TypeDecorator) type in use when producing the
- CHECK constraint for the "non native" type. This so that the custom type
- isn't involved in the expression within the CHECK, since this
- expression is against the "impl" value and not the "decorated" value.
-
- .. change::
- :tags: bug, postgresql
- :tickets: 2844
- :versions: 0.9.0b1
-
- Removed a 128-character truncation from the reflection of the
- server default for a column; this code was original from
- PG system views which truncated the string for readability.
-
- .. change::
- :tags: bug, mysql
- :tickets: 2721, 2839
- :versions: 0.9.0b1
-
- The change in :ticket:`2721`, which is that the ``deferrable`` keyword
- of :class:`_schema.ForeignKeyConstraint` is silently ignored on the MySQL
- backend, will be reverted as of 0.9; this keyword will now render again, raising
- errors on MySQL as it is not understood - the same behavior will also
- apply to the ``initially`` keyword. In 0.8, the keywords will remain
- ignored but a warning is emitted. Additionally, the ``match`` keyword
- now raises a :exc:`.CompileError` on 0.9 and emits a warning on 0.8;
- this keyword is not only silently ignored by MySQL but also breaks
- the ON UPDATE/ON DELETE options.
-
- To use a :class:`_schema.ForeignKeyConstraint`
- that does not render or renders differently on MySQL, use a custom
- compilation option. An example of this usage has been added to the
- documentation, see :ref:`mysql_foreign_keys`.
-
- .. change::
- :tags: bug, sql
- :tickets: 2825
- :versions: 0.9.0b1
-
- The ``.unique`` flag on :class:`.Index` could be produced as ``None``
- if it was generated from a :class:`_schema.Column` that didn't specify ``unique``
- (where it defaults to ``None``). The flag will now always be ``True`` or
- ``False``.
-
- .. change::
- :tags: feature, orm
- :tickets: 2836
- :versions: 0.9.0b1
-
- Added new option to :func:`_orm.relationship` ``distinct_target_key``.
- This enables the subquery eager loader strategy to apply a DISTINCT
- to the innermost SELECT subquery, to assist in the case where
- duplicate rows are generated by the innermost query which corresponds
- to this relationship (there's not yet a general solution to the issue
- of dupe rows within subquery eager loading, however, when joins outside
- of the innermost subquery produce dupes). When the flag
- is set to ``True``, the DISTINCT is rendered unconditionally, and when
- it is set to ``None``, DISTINCT is rendered if the innermost relationship
- targets columns that do not comprise a full primary key.
- The option defaults to False in 0.8 (e.g. off by default in all cases),
- None in 0.9 (e.g. automatic by default). Thanks to Alexander Koval
- for help with this.
-
- .. seealso::
-
- :ref:`change_2836`
-
- .. change::
- :tags: bug, mysql
- :tickets: 2515
- :versions: 0.9.0b1
-
- MySQL-connector dialect now allows options in the create_engine
- query string to override those defaults set up in the connect,
- including "buffered" and "raise_on_warnings".
-
- .. change::
- :tags: bug, postgresql
- :tickets: 2742
- :versions: 0.9.0b1
-
- Parenthesis will be applied to a compound SQL expression as
- rendered in the column list of a CREATE INDEX statement.
-
- .. change::
- :tags: bug, sql
- :tickets: 2742
- :versions: 0.9.0b1
-
- Fixed bug in default compiler plus those of postgresql, mysql, and
- mssql to ensure that any literal SQL expression values are
- rendered directly as literals, instead of as bound parameters,
- within a CREATE INDEX statement. This also changes the rendering
- scheme for other DDL such as constraints.
-
- .. change::
- :tags: bug, sql
- :tickets: 2815
- :versions: 0.9.0b1
-
- A :func:`_expression.select` that is made to refer to itself in its FROM clause,
- typically via in-place mutation, will raise an informative error
- message rather than causing a recursion overflow.
-
- .. change::
- :tags: bug, orm
- :tickets: 2813
- :versions: 0.9.0b1
-
- Fixed bug where using an annotation such as :func:`.remote` or
- :func:`.foreign` on a :class:`_schema.Column` before association with a parent
- :class:`_schema.Table` could produce issues related to the parent table not
- rendering within joins, due to the inherent copy operation performed
- by an annotation.
-
- .. change::
- :tags: bug, sql
- :tickets: 2831
-
- Non-working "schema" argument on :class:`_schema.ForeignKey` is deprecated;
- raises a warning. Removed in 0.9.
-
- .. change::
- :tags: bug, postgresql
- :tickets: 2819
- :versions: 0.9.0b1
-
- Fixed bug where PostgreSQL version strings that had a prefix preceding
- the words "PostgreSQL" or "EnterpriseDB" would not parse.
- Courtesy Scott Schaefer.
-
- .. change::
- :tags: feature, engine
- :tickets: 2821
- :versions: 0.9.0b1
-
- ``repr()`` for the :class:`.URL` of an :class:`_engine.Engine`
- will now conceal the password using asterisks.
- Courtesy Gunnlaugur Þór Briem.
-
- .. change::
- :tags: bug, orm
- :tickets: 2818
- :versions: 0.9.0b1
-
- Fixed bug where :meth:`_query.Query.exists` failed to work correctly
- without any WHERE criterion. Courtesy Vladimir Magamedov.
-
- .. change::
- :tags: bug, sql
- :tickets: 2811
- :versions: 0.9.0b1
-
- Fixed bug where using the ``column_reflect`` event to change the ``.key``
- of the incoming :class:`_schema.Column` would prevent primary key constraints,
- indexes, and foreign key constraints from being correctly reflected.
-
- .. change::
- :tags: feature
- :versions: 0.9.0b1
-
- Added a new flag ``system=True`` to :class:`_schema.Column`, which marks
- the column as a "system" column which is automatically made present
- by the database (such as PostgreSQL ``oid`` or ``xmin``). The
- column will be omitted from the ``CREATE TABLE`` statement but will
- otherwise be available for querying. In addition, the
- :class:`.CreateColumn` construct can be applied to a custom
- compilation rule which allows skipping of columns, by producing
- a rule that returns ``None``.
-
- .. change::
- :tags: bug, orm
- :tickets: 2779
-
- Backported a change from 0.9 whereby the iteration of a hierarchy
- of mappers used in polymorphic inheritance loads is sorted,
- which allows the SELECT statements generated for polymorphic queries
- to have deterministic rendering, which in turn helps with caching
- schemes that cache on the SQL string itself.
-
- .. change::
- :tags: bug, orm
- :tickets: 2794
- :versions: 0.9.0b1
-
- Fixed a potential issue in an ordered sequence implementation used
- by the ORM to iterate mapper hierarchies; under the Jython interpreter
- this implementation wasn't ordered, even though cPython and PyPy
- maintained ordering.
-
- .. change::
- :tags: bug, examples
- :versions: 0.9.0b1
-
- Added "autoincrement=False" to the history table created in the
- versioning example, as this table shouldn't have autoinc on it
- in any case, courtesy Patrick Schmid.
-
- .. change::
- :tags: bug, sql
- :versions: 0.9.0b1
-
- The :meth:`.ColumnOperators.notin_` operator added in 0.8 now properly
- produces the negation of the expression "IN" returns
- when used against an empty collection.
-
- .. change::
- :tags: feature, examples
- :versions: 0.9.0b1
-
- Improved the examples in ``examples/generic_associations``, including
- that ``discriminator_on_association.py`` makes use of single table
- inheritance do the work with the "discriminator". Also
- added a true "generic foreign key" example, which works similarly
- to other popular frameworks in that it uses an open-ended integer
- to point to any other table, foregoing traditional referential
- integrity. While we don't recommend this pattern, information wants
- to be free.
-
- .. change::
- :tags: feature, orm, declarative
- :versions: 0.9.0b1
-
- Added a convenience class decorator :func:`.as_declarative`, is
- a wrapper for :func:`.declarative_base` which allows an existing base
- class to be applied using a nifty class-decorated approach.
-
- .. change::
- :tags: bug, orm
- :tickets: 2786
- :versions: 0.9.0b1
-
- Fixed bug in ORM-level event registration where the "raw" or
- "propagate" flags could potentially be mis-configured in some
- "unmapped base class" configurations.
-
- .. change::
- :tags: bug, orm
- :tickets: 2778
- :versions: 0.9.0b1
-
- A performance fix related to the usage of the :func:`.defer` option
- when loading mapped entities. The function overhead of applying
- a per-object deferred callable to an instance at load time was
- significantly higher than that of just loading the data from the row
- (note that ``defer()`` is meant to reduce DB/network overhead, not
- necessarily function call count); the function call overhead is now
- less than that of loading data from the column in all cases. There
- is also a reduction in the number of "lazy callable" objects created
- per load from N (total deferred values in the result) to 1 (total
- number of deferred cols).
-
- .. change::
- :tags: bug, sqlite
- :tickets: 2781
- :versions: 0.9.0b1
-
- The newly added SQLite DATETIME arguments storage_format and
- regexp apparently were not fully implemented correctly; while the
- arguments were accepted, in practice they would have no effect;
- this has been fixed.
-
- .. change::
- :tags: bug, sql, postgresql
- :tickets: 2780
- :versions: 0.9.0b1
-
- Fixed bug where the expression system relied upon the ``str()``
- form of a some expressions when referring to the ``.c`` collection
- on a ``select()`` construct, but the ``str()`` form isn't available
- since the element relies on dialect-specific compilation constructs,
- notably the ``__getitem__()`` operator as used with a PostgreSQL
- ``ARRAY`` element. The fix also adds a new exception class
- :exc:`.UnsupportedCompilationError` which is raised in those cases
- where a compiler is asked to compile something it doesn't know
- how to.
-
- .. change::
- :tags: bug, engine, oracle
- :tickets: 2776
- :versions: 0.9.0b1
-
- Dialect.initialize() is not called a second time if an :class:`_engine.Engine`
- is recreated, due to a disconnect error. This fixes a particular
- issue in the Oracle 8 dialect, but in general the dialect.initialize()
- phase should only be once per dialect.
-
- .. change::
- :tags: feature, sql
- :tickets: 722
-
- Added new method to the :func:`_expression.insert` construct
- :meth:`_expression.Insert.from_select`. Given a list of columns and
- a selectable, renders ``INSERT INTO (table) (columns) SELECT ..``.
-
- .. change::
- :tags: feature, sql
- :versions: 0.9.0b1
-
- The :func:`_expression.update`, :func:`_expression.insert`, and :func:`_expression.delete` constructs
- will now interpret ORM entities as target tables to be operated upon,
- e.g.::
-
- from sqlalchemy import insert, update, delete
-
- ins = insert(SomeMappedClass).values(x=5)
-
- del_ = delete(SomeMappedClass).where(SomeMappedClass.id == 5)
-
- upd = update(SomeMappedClass).where(SomeMappedClass.id == 5).values(name="ed")
-
- .. change::
- :tags: bug, orm
- :tickets: 2773
- :versions: 0.9.0b1
-
- Fixed bug whereby attribute history functions would fail
- when an object we moved from "persistent" to "pending"
- using the :func:`.make_transient` function, for operations
- involving collection-based backrefs.
-
- .. change::
- :tags: bug, engine, pool
- :tickets: 2772
- :versions: 0.9.0b1
-
- Fixed bug where :class:`.QueuePool` would lose the correct
- checked out count if an existing pooled connection failed to reconnect
- after an invalidate or recycle event.
-
-.. changelog::
- :version: 0.8.2
- :released: July 3, 2013
-
- .. change::
- :tags: bug, mysql
- :tickets: 2768
- :versions: 0.9.0b1
-
- Fixed bug when using multi-table UPDATE where a supplemental
- table is a SELECT with its own bound parameters, where the positioning
- of the bound parameters would be reversed versus the statement
- itself when using MySQL's special syntax.
-
- .. change::
- :tags: bug, sqlite
- :tickets: 2764
- :versions: 0.9.0b1
-
- Added :class:`sqlalchemy.types.BIGINT` to the list of type names that can be
- reflected by the SQLite dialect; courtesy Russell Stuart.
-
- .. change::
- :tags: feature, orm, declarative
- :tickets: 2761
- :versions: 0.9.0b1
-
- ORM descriptors such as hybrid properties can now be referenced
- by name in a string argument used with ``order_by``,
- ``primaryjoin``, or similar in :func:`_orm.relationship`,
- in addition to column-bound attributes.
-
- .. change::
- :tags: feature, firebird
- :tickets: 2763
- :versions: 0.9.0b1
-
- Added new flag ``retaining=True`` to the kinterbasdb and fdb dialects.
- This controls the value of the ``retaining`` flag sent to the
- ``commit()`` and ``rollback()`` methods of the DBAPI connection.
- Due to historical concerns, this flag defaults to ``True`` in 0.8.2,
- however in 0.9.0b1 this flag defaults to ``False``.
-
- .. change::
- :tags: requirements
- :versions: 0.9.0b1
-
- The Python `mock <https://pypi.org/project/mock>`_ library
- is now required in order to run the unit test suite. While part
- of the standard library as of Python 3.3, previous Python installations
- will need to install this in order to run unit tests or to
- use the ``sqlalchemy.testing`` package for external dialects.
-
- .. change::
- :tags: bug, orm
- :tickets: 2750
- :versions: 0.9.0b1
-
- A warning is emitted when trying to flush an object of an inherited
- class where the polymorphic discriminator has been assigned
- to a value that is invalid for the class.
-
- .. change::
- :tags: bug, postgresql
- :tickets: 2740
- :versions: 0.9.0b1
-
- The behavior of :func:`.extract` has been simplified on the
- PostgreSQL dialect to no longer inject a hardcoded ``::timestamp``
- or similar cast into the given expression, as this interfered
- with types such as timezone-aware datetimes, but also
- does not appear to be at all necessary with modern versions
- of psycopg2.
-
-
- .. change::
- :tags: bug, firebird
- :tickets: 2757
- :versions: 0.9.0b1
-
- Type lookup when reflecting the Firebird types LONG and
- INT64 has been fixed so that LONG is treated as INTEGER,
- INT64 treated as BIGINT, unless the type has a "precision"
- in which case it's treated as NUMERIC. Patch courtesy
- Russell Stuart.
-
- .. change::
- :tags: bug, postgresql
- :tickets: 2766
- :versions: 0.9.0b1
-
- Fixed bug in HSTORE type where keys/values that contained
- backslashed quotes would not be escaped correctly when
- using the "non native" (i.e. non-psycopg2) means
- of translating HSTORE data. Patch courtesy Ryan Kelly.
-
- .. change::
- :tags: bug, postgresql
- :tickets: 2767
- :versions: 0.9.0b1
-
- Fixed bug where the order of columns in a multi-column
- PostgreSQL index would be reflected in the wrong order.
- Courtesy Roman Podolyaka.
-
- .. change::
- :tags: bug, sql
- :tickets: 2746, 2668
- :versions: 0.9.0b1
-
- Multiple fixes to the correlation behavior of
- :class:`_expression.Select` constructs, first introduced in 0.8.0:
-
- * To satisfy the use case where FROM entries should be
- correlated outwards to a SELECT that encloses another,
- which then encloses this one, correlation now works
- across multiple levels when explicit correlation is
- established via :meth:`_expression.Select.correlate`, provided
- that the target select is somewhere along the chain
- contained by a WHERE/ORDER BY/columns clause, not
- just nested FROM clauses. This makes
- :meth:`_expression.Select.correlate` act more compatibly to
- that of 0.7 again while still maintaining the new
- "smart" correlation.
-
- * When explicit correlation is not used, the usual
- "implicit" correlation limits its behavior to just
- the immediate enclosing SELECT, to maximize compatibility
- with 0.7 applications, and also prevents correlation
- across nested FROMs in this case, maintaining compatibility
- with 0.8.0/0.8.1.
-
- * The :meth:`_expression.Select.correlate_except` method was not
- preventing the given FROM clauses from correlation in
- all cases, and also would cause FROM clauses to be incorrectly
- omitted entirely (more like what 0.7 would do),
- this has been fixed.
-
- * Calling `select.correlate_except(None)` will enter
- all FROM clauses into correlation as would be expected.
-
- .. change::
- :tags: bug, ext
- :versions: 0.9.0b1
-
- Fixed bug whereby if a composite type were set up
- with a function instead of a class, the mutable extension
- would trip up when it tried to check that column
- for being a :class:`.MutableComposite` (which it isn't).
- Courtesy asldevi.
-
- .. change::
- :tags: feature, sql
- :tickets: 2744, 2734
-
- Provided a new attribute for :class:`.TypeDecorator`
- called :attr:`.TypeDecorator.coerce_to_is_types`,
- to make it easier to control how comparisons using
- ``==`` or ``!=`` to ``None`` and boolean types goes
- about producing an ``IS`` expression, or a plain
- equality expression with a bound parameter.
-
- .. change::
- :tags: feature, postgresql
- :versions: 0.9.0b1
-
- Support for PostgreSQL 9.2 range types has been added.
- Currently, no type translation is provided, so works
- directly with strings or psycopg2 2.5 range extension types
- at the moment. Patch courtesy Chris Withers.
-
- .. change::
- :tags: bug, examples
- :versions: 0.9.0b1
-
- Fixed an issue with the "versioning" recipe whereby a many-to-one
- reference could produce a meaningless version for the target,
- even though it was not changed, when backrefs were present.
- Patch courtesy Matt Chisholm.
-
- .. change::
- :tags: feature, postgresql
- :tickets: 2072
- :versions: 0.9.0b1
-
- Added support for "AUTOCOMMIT" isolation when using the psycopg2
- DBAPI. The keyword is available via the ``isolation_level``
- execution option. Patch courtesy Roman Podolyaka.
-
- .. change::
- :tags: bug, orm
- :tickets: 2759
- :versions: 0.9.0b1
-
- Fixed bug in polymorphic SQL generation where multiple joined-inheritance
- entities against the same base class joined to each other as well
- would not track columns on the base table independently of each other if
- the string of joins were more than two entities long.
-
- .. change::
- :tags: bug, engine
- :versions: 0.9.0b1
-
- Fixed bug where the ``reset_on_return`` argument to various :class:`_pool.Pool`
- implementations would not be propagated when the pool was regenerated.
- Courtesy Eevee.
-
- .. change::
- :tags: bug, orm
- :tickets: 2754
- :versions: 0.9.0b1
-
- Fixed bug where sending a composite attribute into :meth:`_query.Query.order_by`
- would produce a parenthesized expression not accepted by some databases.
-
- .. change::
- :tags: bug, orm
- :tickets: 2755
- :versions: 0.9.0b1
-
- Fixed the interaction between composite attributes and
- the :func:`.aliased` function. Previously, composite attributes
- wouldn't work correctly in comparison operations when aliasing
- was applied.
-
- .. change::
- :tags: bug, mysql
- :tickets: 2715
- :versions: 0.9.0b1
-
- Added another conditional to the ``mysql+gaerdbms`` dialect to
- detect so-called "development" mode, where we should use the
- ``rdbms_mysqldb`` DBAPI. Patch courtesy Brett Slatkin.
-
- .. change::
- :tags: feature, mysql
- :tickets: 2704
- :versions: 0.9.0b1
-
- The ``mysql_length`` parameter used with :class:`.Index` can now
- be passed as a dictionary of column names/lengths, for use
- with composite indexes. Big thanks to Roman Podolyaka for the
- patch.
-
- .. change::
- :tags: bug, mssql
- :tickets: 2747
- :versions: 0.9.0b1
-
- When querying the information schema on SQL Server 2000, removed
- a CAST call that was added in 0.8.1 to help with driver issues,
- which apparently is not compatible on 2000.
- The CAST remains in place for SQL Server 2005 and greater.
-
- .. change::
- :tags: bug, mysql
- :tickets: 2721
- :versions: 0.9.0b1
-
- The ``deferrable`` keyword argument on :class:`_schema.ForeignKey` and
- :class:`_schema.ForeignKeyConstraint` will not render the ``DEFERRABLE`` keyword
- on the MySQL dialect. For a long time we left this in place because
- a non-deferrable foreign key would act very differently than a deferrable
- one, but some environments just disable FKs on MySQL, so we'll be less
- opinionated here.
-
- .. change::
- :tags: bug, ext, orm
- :tickets: 2730
- :versions: 0.9.0b1
-
- Fixed bug where :class:`.MutableDict` didn't report a change event
- when ``clear()`` was called.
-
- .. change::
- :tags: bug, sql
- :tickets: 2738
- :versions: 0.9.0b1
-
- Fixed bug whereby joining a select() of a table "A" with multiple
- foreign key paths to a table "B", to that table "B", would fail
- to produce the "ambiguous join condition" error that would be
- reported if you join table "A" directly to "B"; it would instead
- produce a join condition with multiple criteria.
-
- .. change::
- :tags: bug, sql, reflection
- :tickets: 2728
- :versions: 0.9.0b1
-
- Fixed bug whereby using :meth:`_schema.MetaData.reflect` across a remote
- schema as well as a local schema could produce wrong results
- in the case where both schemas had a table of the same name.
-
- .. change::
- :tags: bug, sql
- :tickets: 2726
- :versions: 0.9.0b1
-
- Removed the "not implemented" ``__iter__()`` call from the base
- :class:`.ColumnOperators` class, while this was introduced
- in 0.8.0 to prevent an endless, memory-growing loop when one also
- implements a ``__getitem__()`` method on a custom
- operator and then calls erroneously ``list()`` on that object,
- it had the effect of causing column elements to report that they
- were in fact iterable types which then throw an error when you try
- to iterate. There's no real way to have both sides here so we
- stick with Python best practices. Careful with implementing
- ``__getitem__()`` on your custom operators!
-
- .. change::
- :tags: feature, orm
- :tickets: 2736
-
- Added a new method :meth:`_query.Query.select_entity_from` which
- will in 0.9 replace part of the functionality of
- :meth:`_query.Query.select_from`. In 0.8, the two methods perform
- the same function, so that code can be migrated to use the
- :meth:`_query.Query.select_entity_from` method as appropriate.
- See the 0.9 migration guide for details.
-
- .. change::
- :tags: bug, orm
- :tickets: 2737
-
- Fixed a regression caused by :ticket:`2682` whereby the
- evaluation invoked by :meth:`_query.Query.update` and :meth:`_query.Query.delete`
- would hit upon unsupported ``True`` and ``False`` symbols
- which now appear due to the usage of ``IS``.
-
- .. change::
- :tags: bug, postgresql
- :tickets: 2735
-
- Fixed the HSTORE type to correctly encode/decode for unicode.
- This is always on, as the hstore is a textual type, and
- matches the behavior of psycopg2 when using Python 3.
- Courtesy Dmitry Mugtasimov.
-
- .. change::
- :tags: bug, examples
-
- Fixed a small bug in the dogpile example where the generation
- of SQL cache keys wasn't applying deduping labels to the
- statement the same way :class:`_query.Query` normally does.
-
- .. change::
- :tags: bug, engine, sybase
- :tickets: 2732
-
- Fixed a bug where the routine to detect the correct kwargs
- being sent to :func:`_sa.create_engine` would fail in some cases,
- such as with the Sybase dialect.
-
- .. change::
- :tags: bug, orm
- :tickets: 2481
-
- Fixed a regression from 0.7 caused by this ticket, which
- made the check for recursion overflow in self-referential
- eager joining too loose, missing a particular circumstance
- where a subclass had lazy="joined" or "subquery" configured
- and the load was a "with_polymorphic" against the base.
-
- .. change::
- :tags: bug, orm
- :tickets: 2718
-
- Fixed a regression from 0.7 where the contextmanager feature
- of :meth:`.Session.begin_nested` would fail to correctly
- roll back the transaction when a flush error occurred, instead
- raising its own exception while leaving the session still
- pending a rollback.
-
- .. change::
- :tags: bug, mysql
-
- Updated mysqlconnector dialect to check for disconnect based
- on the apparent string message sent in the exception; tested
- against mysqlconnector 1.0.9.
-
- .. change::
- :tags: bug, sql, mssql
- :tickets: 2682
-
- Regression from this ticket caused the unsupported keyword
- "true" to render, added logic to convert this to 1/0
- for SQL server.
-
-.. changelog::
- :version: 0.8.1
- :released: April 27, 2013
-
- .. change::
- :tags: bug, orm
- :tickets: 2698
-
- Fixes to the ``sqlalchemy.ext.serializer`` extension, including
- that the "id" passed from the pickler is turned into a string
- to prevent against bytes being parsed on Py3K, as well as that
- ``relationship()`` and ``orm.join()`` constructs are now properly
- serialized.
-
- .. change::
- :tags: bug, orm
- :tickets: 2714
-
- A significant improvement to the inner workings of query.join(),
- such that the decisionmaking involved on how to join has been
- dramatically simplified. New test cases now pass such as
- multiple joins extending from the middle of an already complex
- series of joins involving inheritance and such. Joining from
- deeply nested subquery structures is still complicated and
- not without caveats, but with these improvements the edge
- cases are hopefully pushed even farther out to the edges.
-
- .. change::
- :tags: feature, orm
- :tickets: 2673
-
- Added a convenience method to Query that turns a query into an
- EXISTS subquery of the form
- ``EXISTS (SELECT 1 FROM ... WHERE ...)``.
-
- .. change::
- :tags: bug, orm
-
- Added a conditional to the unpickling process for ORM
- mapped objects, such that if the reference to the object
- were lost when the object was pickled, we don't
- erroneously try to set up _sa_instance_state - fixes
- a NoneType error.
-
- .. change::
- :tags: bug, postgresql
- :tickets: 2712
-
- Opened up the checking for "disconnect" with psycopg2/libpq
- to check for all the various "disconnect" messages within
- the full exception hierarchy. Specifically the
- "closed the connection unexpectedly" message has now been
- seen in at least three different exception types.
- Courtesy Eli Collins.
-
- .. change::
- :tags: bug, sql, mysql
- :tickets: 2682
-
- Fully implemented the IS and IS NOT operators with
- regards to the True/False constants. An expression like
- ``col.is_(True)`` will now render ``col IS true``
- on the target platform, rather than converting the True/
- False constant to an integer bound parameter.
- This allows the ``is_()`` operator to work on MySQL when
- given True/False constants.
-
- .. change::
- :tags: bug, postgresql
- :tickets: 2681
-
- The operators for the PostgreSQL ARRAY type supports
- input types of sets, generators, etc. even when
- a dimension is not specified, by turning the given
- iterable into a collection unconditionally.
-
- .. change::
- :tags: bug, mysql
-
- Fixes to support the latest cymysql DBAPI, courtesy
- Hajime Nakagami.
-
- .. change::
- :tags: bug, mysql
- :tickets: 2663
-
- Improvements to the operation of the pymysql dialect on
- Python 3, including some important decode/bytes steps.
- Issues remain with BLOB types due to driver issues.
- Courtesy Ben Trofatter.
-
- .. change::
- :tags: bug, orm
- :tickets: 2710
-
- Fixed bug where many-to-many relationship with uselist=False
- would fail to delete the association row and raise an error
- if the scalar attribute were set to None. This was a
- regression introduced by the changes for :ticket:`2229`.
-
- .. change::
- :tags: bug, orm
- :tickets: 2708
-
- Improved the behavior of instance management regarding
- the creation of strong references within the Session;
- an object will no longer have an internal reference cycle
- created if it's in the transient state or moves into the
- detached state - the strong ref is created only when the
- object is attached to a Session and is removed when the
- object is detached. This makes it somewhat safer for an
- object to have a `__del__()` method, even though this is
- not recommended, as relationships with backrefs produce
- cycles too. A warning has been added when a class with
- a `__del__()` method is mapped.
-
- .. change::
- :tags: bug, sql
- :tickets: 2702
-
- A major fix to the way in which a select() object produces
- labeled columns when apply_labels() is used; this mode
- produces a SELECT where each column is labeled as in
- <tablename>_<columnname>, to remove column name collisions
- for a multiple table select. The fix is that if two labels
- collide when combined with the table name, i.e.
- "foo.bar_id" and "foo_bar.id", anonymous aliasing will be
- applied to one of the dupes. This allows the ORM to handle
- both columns independently; previously, 0.7
- would in some cases silently emit a second SELECT for the
- column that was "duped", and in 0.8 an ambiguous column error
- would be emitted. The "keys" applied to the .c. collection
- of the select() will also be deduped, so that the "column
- being replaced" warning will no longer emit for any select()
- that specifies use_labels, though the dupe key will be given
- an anonymous label which isn't generally user-friendly.
-
- .. change::
- :tags: bug, mysql
-
- Updated a regexp to correctly extract error code on
- google app engine v1.7.5 and newer. Courtesy
- Dan Ring.
-
- .. change::
- :tags: bug, examples
-
- Fixed a long-standing bug in the caching example, where
- the limit/offset parameter values wouldn't be taken into
- account when computing the cache key. The
- _key_from_query() function has been simplified to work
- directly from the final compiled statement in order to get
- at both the full statement as well as the fully processed
- parameter list.
-
- .. change::
- :tags: bug, mssql
- :tickets: 2355
-
- Part of a longer series of fixes needed for pyodbc+
- mssql, a CAST to NVARCHAR(max) has been added to the bound
- parameter for the table name and schema name in all information schema
- queries to avoid the issue of comparing NVARCHAR to NTEXT,
- which seems to be rejected by the ODBC driver in some cases,
- such as FreeTDS (0.91 only?) plus unicode bound parameters being passed.
- The issue seems to be specific to the SQL Server information
- schema tables and the workaround is harmless for those cases
- where the problem doesn't exist in the first place.
-
- .. change::
- :tags: bug, sql
- :tickets: 2691
-
- Fixed bug where disconnect detect on error would
- raise an attribute error if the error were being
- raised after the Connection object had already
- been closed.
-
- .. change::
- :tags: bug, sql
- :tickets: 2703
-
- Reworked internal exception raises that emit
- a rollback() before re-raising, so that the stack
- trace is preserved from sys.exc_info() before entering
- the rollback. This so that the traceback is preserved
- when using coroutine frameworks which may have switched
- contexts before the rollback function returns.
-
- .. change::
- :tags: bug, orm
- :tickets: 2697
-
- Fixed bug whereby ORM would run the wrong kind of
- query when refreshing an inheritance-mapped class
- where the superclass was mapped to a non-Table
- object, like a custom join() or a select(),
- running a query that assumed a hierarchy that's
- mapped to individual Table-per-class.
-
- .. change::
- :tags: bug, orm
-
- Fixed `__repr__()` on mapper property constructs
- to work before the object is initialized, so
- that Sphinx builds with recent Sphinx versions
- can read them.
-
- .. change::
- :tags: bug, sql, postgresql
-
- The _Binary base type now converts values through
- the bytes() callable when run on Python 3; in particular
- psycopg2 2.5 with Python 3.3 seems to now be returning
- the "memoryview" type, so this is converted to bytes
- before return.
-
- .. change::
- :tags: bug, sql
- :tickets: 2695
-
- Improvements to Connection auto-invalidation
- handling. If a non-disconnect error occurs,
- but leads to a delayed disconnect error within error
- handling (happens with MySQL), the disconnect condition
- is detected. The Connection can now also be closed
- when in an invalid state, meaning it will raise "closed"
- on next usage, and additionally the "close with result"
- feature will work even if the autorollback in an error
- handling routine fails and regardless of whether the
- condition is a disconnect or not.
-
-
- .. change::
- :tags: bug, orm, declarative
- :tickets: 2656
-
- Fixed indirect regression regarding :func:`.has_inherited_table`,
- where since it considers the current class' ``__table__``, was
- sensitive to when it was called. This is 0.7's behavior also,
- but in 0.7 things tended to "work out" within events like
- ``__mapper_args__()``. :func:`.has_inherited_table` now only
- considers superclasses, so should return the same answer
- regarding the current class no matter when it's called
- (obviously assuming the state of the superclass).
-
- .. change::
- :tags: bug, mssql
-
- Added support for additional "disconnect" messages
- to the pymssql dialect. Courtesy John Anderson.
-
- .. change::
- :tags: feature, sql
-
- Loosened the check on dialect-specific argument names
- passed to Table(); since we want to support external dialects
- and also want to support args without a certain dialect
- being installed, it only checks the format of the arg now,
- rather than looking for that dialect in sqlalchemy.dialects.
-
- .. change::
- :tags: bug, sql
-
- Fixed bug whereby a DBAPI that can return "0"
- for cursor.lastrowid would not function correctly
- in conjunction with :attr:`_engine.ResultProxy.inserted_primary_key`.
-
- .. change::
- :tags: bug, mssql
- :tickets: 2683
-
- Fixed Py3K bug regarding "binary" types and
- pymssql. Courtesy Marc Abramowitz.
-
- .. change::
- :tags: bug, postgresql
- :tickets: 2680
-
- Added missing HSTORE type to postgresql type names
- so that the type can be reflected.
-
-.. changelog::
- :version: 0.8.0
- :released: March 9, 2013
-
- .. note::
-
- There are some new behavioral changes as of 0.8.0
- not present in 0.8.0b2. They are present in the
- migration document as follows:
-
- * :ref:`legacy_is_orphan_addition`
-
- * :ref:`metadata_create_drop_tables`
-
- * :ref:`correlation_context_specific`
-
- .. change::
- :tags: feature, orm
- :tickets: 2675
-
- A meaningful :attr:`.QueryableAttribute.info` attribute is
- added, which proxies down to the ``.info`` attribute on either
- the :class:`_schema.Column` object if directly present, or
- the :class:`.MapperProperty` otherwise. The full behavior
- is documented and ensured by tests to remain stable.
-
- .. change::
- :tags: bug, sql
- :tickets: 2668
-
- The behavior of SELECT correlation has been improved such that
- the :meth:`_expression.Select.correlate` and :meth:`_expression.Select.correlate_except`
- methods, as well as their ORM analogues, will still retain
- "auto-correlation" behavior in that the FROM clause is modified
- only if the output would be legal SQL; that is, the FROM clause
- is left intact if the correlated SELECT is not used in the context
- of an enclosing SELECT inside of the WHERE, columns, or HAVING clause.
- The two methods now only specify conditions to the default
- "auto correlation", rather than absolute FROM lists.
-
- .. change::
- :tags: feature, mysql
-
- New dialect for CyMySQL added, courtesy Hajime Nakagami.
-
- .. change::
- :tags: bug, orm
- :tickets: 2674
-
- Improved checking for an existing backref name conflict during
- mapper configuration; will now test for name conflicts on
- superclasses and subclasses, in addition to the current mapper,
- as these conflicts break things just as much. This is new for
- 0.8, but see below for a warning that will also be triggered
- in 0.7.11.
-
- .. change::
- :tags: bug, orm
- :tickets: 2674
-
- Improved the error message emitted when a "backref loop" is detected,
- that is when an attribute event triggers a bidirectional
- assignment between two other attributes with no end.
- This condition can occur not just when an object of the wrong
- type is assigned, but also when an attribute is mis-configured
- to backref into an existing backref pair. Also in 0.7.11.
-
- .. change::
- :tags: bug, orm
- :tickets: 2674
-
- A warning is emitted when a MapperProperty is assigned to a mapper
- that replaces an existing property, if the properties in question
- aren't plain column-based properties. Replacement of relationship
- properties is rarely (ever?) what is intended and usually refers to a
- mapper mis-configuration. Also in 0.7.11.
-
- .. change::
- :tags: feature, orm
-
- Can set/change the "cascade" attribute on a :func:`_orm.relationship`
- construct after it's been constructed already. This is not
- a pattern for normal use but we like to change the setting
- for demonstration purposes in tutorials.
-
- .. change::
- :tags: bug, schema
- :tickets: 2664
-
- :meth:`_schema.MetaData.create_all` and :meth:`_schema.MetaData.drop_all` will
- now accommodate an empty list as an instruction to not create/drop
- any items, rather than ignoring the collection.
-
-
- .. change::
- :tags: bug, tests
- :tickets: 2669
-
- Fixed an import of "logging" in test_execute which was not
- working on some linux platforms. Also in 0.7.11.
-
- .. change::
- :tags: bug, orm
- :tickets: 2662
-
- A clear error message is emitted if an event handler
- attempts to emit SQL on a Session within the after_commit()
- handler, where there is not a viable transaction in progress.
-
- .. change::
- :tags: bug, orm
- :tickets: 2665
-
- Detection of a primary key change within the process
- of cascading a natural primary key update will succeed
- even if the key is composite and only some of the
- attributes have changed.
-
- .. change::
- :tags: feature, orm
- :tickets: 2658
-
- Added new helper function :func:`.was_deleted`, returns True
- if the given object was the subject of a :meth:`.Session.delete`
- operation.
-
- .. change::
- :tags: bug, orm
- :tickets: 2658
-
- An object that's deleted from a session will be de-associated with
- that session fully after the transaction is committed, that is
- the :func:`.object_session` function will return None.
-
- .. change::
- :tags: bug, oracle
-
- The cx_oracle dialect will no longer run the bind parameter names
- through ``encode()``, as this is not valid on Python 3, and prevented
- statements from functioning correctly on Python 3. We now
- encode only if ``supports_unicode_binds`` is False, which is not
- the case for cx_oracle when at least version 5 of cx_oracle is used.
-
- .. change::
- :tags: bug, orm
- :tickets: 2661
-
- Fixed bug whereby :meth:`_query.Query.yield_per` would set the execution
- options incorrectly, thereby breaking subsequent usage of the
- :meth:`_query.Query.execution_options` method. Courtesy Ryan Kelly.
-
- .. change::
- :tags: bug, orm
- :tickets: 1768
-
- Fixed the consideration of the ``between()`` operator
- so that it works correctly with the new relationship local/remote
- system.
-
- .. change::
- :tags: bug, sql
- :tickets: 2660, 1768
-
- Fixed a bug regarding column annotations which in particular
- could impact some usages of the new :func:`_orm.remote` and
- :func:`_orm.local` annotation functions, where annotations
- could be lost when the column were used in a subsequent
- expression.
-
- .. change::
- :tags: bug, mysql, gae
- :tickets: 2649
-
- Added a conditional import to the ``gaerdbms`` dialect which attempts
- to import rdbms_apiproxy vs. rdbms_googleapi to work
- on both dev and production platforms. Also now honors the
- ``instance`` attribute. Courtesy Sean Lynch.
- Also in 0.7.10.
-
- .. change::
- :tags: bug, sql
- :tickets: 2496
-
- The :meth:`.ColumnOperators.in_` operator will now coerce
- values of ``None`` to :func:`.null`.
-
- .. change::
- :tags: feature, sql
- :tickets: 2657
-
- Added a new argument to :class:`.Enum` and its base
- :class:`.SchemaType` ``inherit_schema``. When set to ``True``,
- the type will set its ``schema`` attribute of that of the
- :class:`_schema.Table` to which it is associated. This also occurs
- during a :meth:`_schema.Table.tometadata` operation; the :class:`.SchemaType`
- is now copied in all cases when :meth:`_schema.Table.tometadata` happens,
- and if ``inherit_schema=True``, the type will take on the new
- schema name passed to the method. The ``schema`` is important
- when used with the PostgreSQL backend, as the type results in
- a ``CREATE TYPE`` statement.
-
- .. change::
- :tags: feature, postgresql
-
- Added :meth:`.postgresql.ARRAY.Comparator.any` and
- :meth:`.postgresql.ARRAY.Comparator.all`
- methods, as well as standalone expression constructs. Big thanks
- to Audrius Kažukauskas for the terrific work here.
-
- .. change::
- :tags: sql, bug
- :tickets: 2643
-
- Fixed bug where :meth:`_schema.Table.tometadata` would fail if a
- :class:`_schema.Column` had both a foreign key as well as an
- alternate ".key" name for the column. Also in 0.7.10.
-
- .. change::
- :tags: sql, bug
- :tickets: 2629
-
- insert().returning() raises an informative CompileError if attempted
- to compile on a dialect that doesn't support RETURNING.
-
- .. change::
- :tags: orm, bug
- :tickets: 2655
-
- the consideration of a pending object as
- an "orphan" has been modified to more closely match the
- behavior as that of persistent objects, which is that the object
- is expunged from the :class:`.Session` as soon as it is
- de-associated from any of its orphan-enabled parents. Previously,
- the pending object would be expunged only if de-associated
- from all of its orphan-enabled parents. The new flag ``legacy_is_orphan``
- is added to :class:`_orm.Mapper` which re-establishes the
- legacy behavior.
-
- See the change note and example case at :ref:`legacy_is_orphan_addition`
- for a detailed discussion of this change.
-
- .. change::
- :tags: orm, bug
- :tickets: 2653
-
- Fixed the (most likely never used) "@collection.link" collection
- method, which fires off each time the collection is associated
- or de-associated with a mapped object - the decorator
- was not tested or functional. The decorator method
- is now named :meth:`.collection.linker` though the name "link"
- remains for backwards compatibility. Courtesy Luca Wehrstedt.
-
- .. change::
- :tags: orm, bug
- :tickets: 2654
-
- Made some fixes to the system of producing custom instrumented
- collections, mainly that the usage of the @collection decorators
- will now honor the __mro__ of the given class, applying the
- logic of the sub-most classes' version of a particular collection
- method. Previously, it wasn't predictable when subclassing
- an existing instrumented class such as :class:`.MappedCollection`
- whether or not custom methods would resolve correctly.
-
- .. change::
- :tags: orm, removed
-
- The undocumented (and hopefully unused) system of producing
- custom collections using an ``__instrumentation__`` datastructure
- associated with the collection has been removed, as this was a complex
- and untested feature which was also essentially redundant versus the
- decorator approach. Other internal simplifications to the
- orm.collections module have been made as well.
-
- .. change::
- :tags: mssql, feature
-
- Added ``mssql_include`` and ``mssql_clustered`` options to
- :class:`.Index`, renders the ``INCLUDE`` and ``CLUSTERED`` keywords,
- respectively. Courtesy Derek Harland.
-
- .. change::
- :tags: sql, feature
- :tickets: 695
-
- :class:`.Index` now supports arbitrary SQL expressions and/or
- functions, in addition to straight columns. Common modifiers
- include using ``somecolumn.desc()`` for a descending index and
- ``func.lower(somecolumn)`` for a case-insensitive index, depending on the
- capabilities of the target backend.
-
- .. change::
- :tags: mssql, bug
- :tickets: 2638
-
- Added a py3K conditional around unnecessary .decode()
- call in mssql information schema, fixes reflection
- in Py3K. Also in 0.7.10.
-
- .. change::
- :tags: orm, bug
- :tickets: 2650
-
- Fixed potential memory leak which could occur if an
- arbitrary number of :class:`.sessionmaker` objects
- were created. The anonymous subclass created by
- the sessionmaker, when dereferenced, would not be garbage
- collected due to remaining class-level references from the
- event package. This issue also applies to any custom system
- that made use of ad-hoc subclasses in conjunction with
- an event dispatcher. Also in 0.7.10.
-
- .. change::
- :tags: mssql, bug
-
- Fixed a regression whereby the "collation" parameter
- of the character types CHAR, NCHAR, etc. stopped working,
- as "collation" is now supported by the base string types.
- The TEXT, NCHAR, CHAR, VARCHAR types within the
- MSSQL dialect are now synonyms for the base types.
-
- .. change::
- :tags: mssql, feature
- :tickets: 2644
-
- DDL for IDENTITY columns is now supported on
- non-primary key columns, by establishing a
- :class:`.Sequence` construct on any
- integer column. Courtesy Derek Harland.
-
- .. change::
- :tags: examples, bug
-
- Fixed a regression in the examples/dogpile_caching example
- which was due to the change in :ticket:`2614`.
-
- .. change::
- :tags: orm, bug
- :tickets: 2640
-
- :meth:`_query.Query.merge_result` can now load rows from an outer join
- where an entity may be ``None`` without throwing an error.
- Also in 0.7.10.
-
- .. change::
- :tags: sql, bug
- :tickets: 2648
-
- Tweaked the "REQUIRED" symbol used by the compiler to identify
- INSERT/UPDATE bound parameters that need to be passed, so that
- it's more easily identifiable when writing custom bind-handling
- code.
-
- .. change::
- :tags: postgresql, bug
-
- Fixed bug in :class:`~sqlalchemy.dialects.postgresql.array()` construct whereby using it
- inside of an :func:`_expression.insert` construct would produce an
- error regarding a parameter issue in the ``self_group()`` method.
-
- .. change::
- :tags: orm, feature
-
- Extended the :doc:`/core/inspection` system so that all Python descriptors
- associated with the ORM or its extensions can be retrieved.
- This fulfills the common request of being able to inspect
- all :class:`.QueryableAttribute` descriptors in addition to
- extension types such as :class:`.hybrid_property` and
- :class:`.AssociationProxy`. See :attr:`_orm.Mapper.all_orm_descriptors`.
-
- .. change::
- :tags: mysql, feature
-
- GAE dialect now accepts username/password arguments in the URL,
- courtesy Owen Nelson.
-
- .. change::
- :tags: mysql, bug
-
- GAE dialect won't fail on None match if the error code can't be extracted
- from the exception throw; courtesy Owen Nelson.
-
- .. change::
- :tags: orm, bug
- :tickets: 2637
-
- Fixes to the "dynamic" loader on :func:`_orm.relationship`, includes
- that backrefs will work properly even when autoflush is disabled,
- history events are more accurate in scenarios where multiple add/remove
- of the same object occurs.
-
-.. changelog::
- :version: 0.8.0b2
- :released: December 14, 2012
-
- .. change::
- :tags: orm, bug
- :tickets: 2635
-
- The :meth:`_query.Query.select_from` method can now be used with a
- :func:`.aliased` construct without it interfering with the entities
- being selected. Basically, a statement like this::
-
- ua = aliased(User)
- session.query(User.name).select_from(ua).join(User, User.name > ua.name)
-
- Will maintain the columns clause of the SELECT as coming from the
- unaliased "user", as specified; the select_from only takes place in the
- FROM clause:
-
- .. sourcecode:: sql
-
- SELECT users.name AS users_name FROM users AS users_1
- JOIN users ON users.name < users_1.name
-
- Note that this behavior is in contrast
- to the original, older use case for :meth:`_query.Query.select_from`, which is that
- of restating the mapped entity in terms of a different selectable::
-
- session.query(User.name).select_from(user_table.select().where(user_table.c.id > 5))
-
- Which produces:
-
- .. sourcecode:: sql
-
- SELECT anon_1.name AS anon_1_name FROM (SELECT users.id AS id,
- users.name AS name FROM users WHERE users.id > :id_1) AS anon_1
-
- It was the "aliasing" behavior of the latter use case that was
- getting in the way of the former use case. The method now
- specifically considers a SQL expression like
- :func:`_expression.select` or :func:`_expression.alias`
- separately from a mapped entity like a :func:`.aliased`
- construct.
-
- .. change::
- :tags: sql, bug
- :tickets: 2633
-
- Fixed a regression caused by :ticket:`2410` whereby a
- :class:`.CheckConstraint` would apply itself back to the
- original table during a :meth:`_schema.Table.tometadata` operation, as
- it would parse the SQL expression for a parent table. The
- operation now copies the given expression to correspond to the
- new table.
-
- .. change::
- :tags: oracle, bug
- :tickets: 2619
-
- Fixed table reflection for Oracle when accessing a synonym that refers
- to a DBLINK remote database; while the syntax has been present in the
- Oracle dialect for some time, up until now it has never been tested.
- The syntax has been tested against a sample database linking to itself,
- however there's still some uncertainty as to what should be used for the
- "owner" when querying the remote database for table information.
- Currently, the value of "username" from user_db_links is used to
- match the "owner".
-
- .. change::
- :tags: orm, feature
- :tickets: 2601
-
- Added :meth:`.KeyedTuple._asdict` and :attr:`.KeyedTuple._fields`
- to the :class:`.KeyedTuple` class to provide some degree of compatibility
- with the Python standard library ``collections.namedtuple()``.
-
- .. change::
- :tags: sql, bug
- :tickets: 2610
-
- Fixed bug whereby using a label_length on dialect that was smaller
- than the size of actual column identifiers would fail to render
- the columns correctly in a SELECT statement.
-
- .. change::
- :tags: sql, feature
- :tickets: 2623
-
- The :class:`_expression.Insert` construct now supports multi-valued inserts,
- that is, an INSERT that renders like
- "INSERT INTO table VALUES (...), (...), ...".
- Supported by PostgreSQL, SQLite, and MySQL.
- Big thanks to Idan Kamara for doing the legwork on this one.
-
- .. seealso::
-
- :ref:`feature_2623`
-
- .. change::
- :tags: oracle, bug
- :tickets: 2620
-
- The Oracle LONG type, while an unbounded text type, does not appear
- to use the cx_Oracle.LOB type when result rows are returned,
- so the dialect has been repaired to exclude LONG from
- having cx_Oracle.LOB filtering applied. Also in 0.7.10.
-
- .. change::
- :tags: oracle, bug
- :tickets: 2611
-
- Repaired the usage of ``.prepare()`` in conjunction with
- cx_Oracle so that a return value of ``False`` will result
- in no call to ``connection.commit()``, hence avoiding
- "no transaction" errors. Two-phase transactions have
- now been shown to work in a rudimental fashion with
- SQLAlchemy and cx_oracle, however are subject to caveats
- observed with the driver; check the documentation
- for details. Also in 0.7.10.
-
- .. change::
- :tags: sql, bug
- :tickets: 2618
-
- The :class:`~sqlalchemy.types.DECIMAL` type now honors the "precision" and
- "scale" arguments when rendering DDL.
-
- .. change::
- :tags: orm, bug
- :tickets: 2624
-
- The :class:`.MutableComposite` type did not allow for the
- :meth:`.MutableBase.coerce` method to be used, even though
- the code seemed to indicate this intent, so this now works
- and a brief example is added. As a side-effect,
- the mechanics of this event handler have been changed so that
- new :class:`.MutableComposite` types no longer add per-type
- global event handlers. Also in 0.7.10.
-
- .. change::
- :tags: sql, bug
- :tickets: 2621
-
- Made an adjustment to the "boolean", (i.e. ``__nonzero__``)
- evaluation of binary expressions, i.e. ``x1 == x2``, such
- that the "auto-grouping" applied by :class:`.BinaryExpression`
- in some cases won't get in the way of this comparison.
- Previously, an expression like::
-
- expr1 = mycolumn > 2
- bool(expr1 == expr1)
-
- Would evaluate as ``False``, even though this is an identity
- comparison, because ``mycolumn > 2`` would be "grouped" before
- being placed into the :class:`.BinaryExpression`, thus changing
- its identity. :class:`.BinaryExpression` now keeps track
- of the "original" objects passed in.
- Additionally the ``__nonzero__`` method now only returns if
- the operator is ``==`` or ``!=`` - all others raise ``TypeError``.
-
- .. change::
- :tags: firebird, bug
- :tickets: 2622
-
- Added missing import for "fdb" to the experimental
- "firebird+fdb" dialect.
-
- .. change::
- :tags: orm, feature
-
- Allow synonyms to be used when defining primary and secondary
- joins for relationships.
-
- .. change::
- :tags: orm, bug
- :tickets: 2614
-
- A second overhaul of aliasing/internal pathing mechanics
- now allows two subclasses to have different relationships
- of the same name, supported with subquery or joined eager
- loading on both simultaneously when a full polymorphic
- load is used.
-
- .. change::
- :tags: orm, bug
- :tickets: 2617
-
- Fixed bug whereby a multi-hop subqueryload within
- a particular with_polymorphic load would produce a KeyError.
- Takes advantage of the same internal pathing overhaul
- as :ticket:`2614`.
-
- .. change::
- :tags: sql, bug
-
- Fixed a gotcha where inadvertently calling list() on a
- :class:`_expression.ColumnElement` would go into an endless loop, if
- :meth:`.ColumnOperators.__getitem__` were implemented.
- A new NotImplementedError is emitted via ``__iter__()``.
-
- .. change::
- :tags: orm, extensions, feature
-
- The :mod:`sqlalchemy.ext.mutable` extension now includes the
- example :class:`.MutableDict` class as part of the extension.
-
- .. change::
- :tags: postgresql, feature
- :tickets: 2606
-
- :class:`.HSTORE` is now available in the PostgreSQL dialect.
- Will also use psycopg2's extensions if available. Courtesy
- Audrius Kažukauskas.
-
- .. change::
- :tags: sybase, feature
- :tickets: 1753
-
- Reflection support has been added to the Sybase dialect.
- Big thanks to Ben Trofatter for all the work developing and
- testing this.
-
- .. change::
- :tags: engine, feature
-
- The :meth:`_engine.Connection.connect` and :meth:`_engine.Connection.contextual_connect`
- methods now return a "branched" version so that the :meth:`_engine.Connection.close`
- method can be called on the returned connection without affecting the
- original. Allows symmetry when using :class:`_engine.Engine` and
- :class:`_engine.Connection` objects as context managers::
-
- with conn.connect() as c: # leaves the Connection open
- c.execute("...")
-
- with engine.connect() as c: # closes the Connection
- c.execute("...")
-
- .. change::
- :tags: engine
-
- The "reflect=True" argument to :class:`~sqlalchemy.schema.MetaData` is deprecated.
- Please use the :meth:`_schema.MetaData.reflect` method.
-
- .. change::
- :tags: sql, bug
- :tickets: 2603
-
- Fixed bug in type_coerce() whereby typing information
- could be lost if the statement were used as a subquery
- inside of another statement, as well as other similar
- situations. Among other things, would cause
- typing information to be lost when the Oracle/mssql dialects
- would apply limit/offset wrappings.
-
- .. change::
- :tags: orm, bug
- :tickets: 2602
-
- Fixed regression where query.update() would produce
- an error if an object matched by the "fetch"
- synchronization strategy wasn't locally present.
- Courtesy Scott Torborg.
-
- .. change::
- :tags: sql, bug
- :tickets: 2597
-
- Fixed bug whereby the ".key" of a Column wasn't being
- used when producing a "proxy" of the column against
- a selectable. This probably didn't occur in 0.7
- since 0.7 doesn't respect the ".key" in a wider
- range of scenarios.
-
- .. change::
- :tags: mssql, feature
- :tickets: 2600
-
- Support for reflection of the "name" of primary key
- constraints added, courtesy Dave Moore.
-
- .. change::
- :tags: informix
-
- Some cruft regarding informix transaction handling has been
- removed, including a feature that would skip calling
- commit()/rollback() as well as some hardcoded isolation level
- assumptions on begin().. The status of this dialect is not
- well understood as we don't have any users working with it,
- nor any access to an Informix database. If someone with
- access to Informix wants to help test this dialect, please
- let us know.
-
- .. change::
- :tags: pool, feature
-
- The :class:`_pool.Pool` will now log all connection.close()
- operations equally, including closes which occur for
- invalidated connections, detached connections, and connections
- beyond the pool capacity.
-
- .. change::
- :tags: pool, feature
- :tickets: 2611
-
- The :class:`_pool.Pool` now consults the :class:`.Dialect` for
- functionality regarding how the connection should be
- "auto rolled back", as well as closed. This grants more
- control of transaction scope to the dialect, so that we
- will be better able to implement transactional workarounds
- like those potentially needed for pysqlite and cx_oracle.
-
- .. change::
- :tags: pool, feature
-
- Added new :meth:`_events.PoolEvents.reset` hook to capture
- the event before a connection is auto-rolled back, upon
- return to the pool. Together with
- :meth:`_events.ConnectionEvents.rollback` this allows all rollback
- events to be intercepted.
-
-.. changelog::
- :version: 0.8.0b1
- :released: October 30, 2012
-
- .. change::
- :tags: sql, bug
- :tickets: 2593
-
- Fixed bug where keyword arguments passed to
- :meth:`.Compiler.process` wouldn't get propagated
- to the column expressions present in the columns
- clause of a SELECT statement. In particular this would
- come up when used by custom compilation schemes that
- relied upon special flags.
-
- .. change::
- :tags: sql, feature
-
- Added a new method :meth:`_engine.Engine.execution_options`
- to :class:`_engine.Engine`. This method works similarly to
- :meth:`_engine.Connection.execution_options` in that it creates
- a copy of the parent object which will refer to the new
- set of options. The method can be used to build
- sharding schemes where each engine shares the same
- underlying pool of connections. The method
- has been tested against the horizontal shard
- recipe in the ORM as well.
-
- .. seealso::
-
- :meth:`_engine.Engine.execution_options`
-
- .. change::
- :tags: sql, orm, bug
- :tickets: 2595
-
- The auto-correlation feature of :func:`_expression.select`, and
- by proxy that of :class:`_query.Query`, will not
- take effect for a SELECT statement that is being
- rendered directly in the FROM list of the enclosing
- SELECT. Correlation in SQL only applies to column
- expressions such as those in the WHERE, ORDER BY,
- columns clause.
-
- .. change::
- :tags: sqlite
- :changeset: c3addcc9ffad
-
- Added :class:`_types.NCHAR`, :class:`_types.NVARCHAR`
- to the SQLite dialect's list of recognized type names
- for reflection. SQLite returns the name given
- to a type as the name returned.
-
- .. change::
- :tags: examples
- :tickets: 2589
-
- The Beaker caching example has been converted
- to use `dogpile.cache <https://dogpilecache.readthedocs.io/>`_.
- This is a new caching library written by the same
- creator of Beaker's caching internals, and represents a
- vastly improved, simplified, and modernized system of caching.
-
- .. seealso::
-
- :ref:`examples_caching`
-
- .. change::
- :tags: general
- :tickets:
-
- SQLAlchemy 0.8 now targets Python 2.5 and
- above. Python 2.4 is no longer supported.
-
- .. change::
- :tags: removed, general
- :tickets: 2433
-
- The "sqlalchemy.exceptions"
- synonym for "sqlalchemy.exc" is removed
- fully.
-
- .. change::
- :tags: removed, orm
- :tickets: 2442
-
- 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.
-
- .. change::
- :tags: orm, moved
- :tickets:
-
- The InstrumentationManager interface
- and the entire related system of alternate
- class implementation is now moved out
- to sqlalchemy.ext.instrumentation. This is
- a seldom used system that adds significant
- complexity and overhead to the mechanics of
- class instrumentation. The new architecture
- allows it to remain unused until
- InstrumentationManager is actually imported,
- at which point it is bootstrapped into
- the core.
-
- .. change::
- :tags: orm, feature
- :tickets: 1401
-
- 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.
-
- .. seealso::
-
- :ref:`feature_relationship_08`
-
- .. change::
- :tags: orm, bug
- :tickets: 2527
-
- ORM will perform extra effort to determine
- that an FK dependency between two tables is
- not significant during flush if the tables
- are related via joined inheritance and the FK
- dependency is not part of the inherit_condition,
- saves the user a use_alter directive.
-
- .. change::
- :tags: orm, feature
- :tickets: 2333
-
- 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.
-
- .. change::
- :tags: orm, feature
- :tickets: 1106, 2438
-
- The of_type() construct on attributes
- now accepts aliased() class constructs as well
- as with_polymorphic constructs, and works with
- query.join(), any(), has(), and also
- eager loaders subqueryload(), joinedload(),
- contains_eager()
-
- .. change::
- :tags: orm, feature
- :tickets: 2585
-
- Improvements to event listening for
- mapped classes allows that unmapped classes
- can be specified for instance- and mapper-events.
- The established events will be automatically
- set up on subclasses of that class when the
- propagate=True flag is passed, and the
- events will be set up for that class itself
- if and when it is ultimately mapped.
-
- .. change::
- :tags: orm, bug
- :tickets: 2590
-
- The instrumentation events class_instrument(),
- class_uninstrument(), and attribute_instrument()
- will now fire off only for descendant classes
- of the class assigned to listen(). Previously,
- an event listener would be assigned to listen
- for all classes in all cases regardless of the
- "target" argument passed.
-
- .. change::
- :tags: orm, bug
- :tickets: 1900
-
- with_polymorphic() produces JOINs
- in the correct order and with correct inheriting
- tables in the case of sending multi-level
- subclasses in an arbitrary order or with
- intermediary classes missing.
-
- .. change::
- :tags: orm, feature
- :tickets: 2485
-
- The "deferred declarative
- reflection" system has been moved into the
- declarative extension itself, using the
- new DeferredReflection class. This
- class is now tested with both single
- and joined table inheritance use cases.
-
- .. change::
- :tags: orm, feature
- :tickets: 2208
-
- 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.
-
- .. change::
- :tags: orm, feature
- :tickets: 2452
-
- Calling rollback() within a
- session.begin_nested() will now only expire
- those objects that had net changes within the
- scope of that transaction, that is objects which
- were dirty or were modified on a flush. This
- allows the typical use case for begin_nested(),
- that of altering a small subset of objects, to
- leave in place the data from the larger enclosing
- set of objects that weren't modified in
- that sub-transaction.
-
- .. change::
- :tags: orm, feature
- :tickets: 2372
-
- Added utility feature
- Session.enable_relationship_loading(),
- supersedes relationship.load_on_pending.
- Both features should be avoided, however.
-
- .. change::
- :tags: orm, feature
- :tickets:
-
- Added support for .info dictionary argument to
- column_property(), relationship(), composite().
- All MapperProperty classes have an auto-creating .info
- dict available overall.
-
- .. change::
- :tags: orm, feature
- :tickets: 2229
-
- Adding/removing None from a mapped collection
- now generates attribute events. Previously, a None
- append would be ignored in some cases. Related
- to.
-
- .. change::
- :tags: orm, feature
- :tickets: 2229
-
- The presence of None in a mapped collection
- now raises an error during flush. Previously,
- None values in collections would be silently ignored.
-
- .. change::
- :tags: orm, feature
- :tickets:
-
- The Query.update() method is now
- more lenient as to the table
- being updated. Plain Table objects are better
- supported now, and additional a joined-inheritance
- subclass may be used with update(); the subclass
- table will be the target of the update,
- and if the parent table is referenced in the
- WHERE clause, the compiler will call upon
- UPDATE..FROM syntax as allowed by the dialect
- to satisfy the WHERE clause. MySQL's multi-table
- update feature is also supported if columns
- are specified by object in the "values" dictionary.
- PG's DELETE..USING is also not available
- in Core yet.
-
- .. change::
- :tags: orm, feature
- :tickets:
-
- New session events after_transaction_create
- and after_transaction_end
- allows tracking of new SessionTransaction objects.
- If the object is inspected, can be used to determine
- when a session first becomes active and when
- it deactivates.
-
- .. change::
- :tags: orm, feature
- :tickets: 2592
-
- The Query can now load entity/scalar-mixed
- "tuple" rows that contain
- types which aren't hashable, by setting the flag
- "hashable=False" on the corresponding TypeEngine object
- in use. Custom types that return unhashable types
- (typically lists) can set this flag to False.
-
- .. change::
- :tags: orm, bug
- :tickets: 2481
-
- Improvements to joined/subquery eager
- loading dealing with chains of subclass entities
- sharing a common base, with no specific "join depth"
- provided. Will chain out to
- each subclass mapper individually before detecting
- a "cycle", rather than considering the base class
- to be the source of the "cycle".
-
- .. change::
- :tags: orm, bug
- :tickets: 2320
-
- 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.
-
- .. change::
- :tags: orm, bug
- :tickets: 2405
-
- 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.
-
- .. change::
- :tags: orm, bug
- :tickets: 2350
-
- Lazy loads emitted within flush events
- such as before_flush(), before_update(),
- etc. will now function as they would
- within non-event code, regarding consideration
- of the PK/FK values used in the lazy-emitted
- query. Previously,
- special flags would be established that
- would cause lazy loads to load related items
- based on the "previous" value of the
- parent PK/FK values specifically when called
- upon within a flush; the signal to load
- in this way is now localized to where the
- unit of work actually needs to load that
- way. Note that the UOW does
- sometimes load these collections before
- the before_update() event is called,
- so the usage of "passive_updates" or not
- can affect whether or not a collection will
- represent the "old" or "new" data, when
- accessed within a flush event, based
- on when the lazy load was emitted.
- The change is backwards incompatible in
- the exceedingly small chance that
- user event code depended on the old
- behavior.
-
- .. change::
- :tags: orm, feature
- :tickets: 2179
-
- 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.
-
- .. change::
- :tags: orm, feature
- :tickets: 2464
-
- The after_attach event is now
- emitted after the object is established
- in Session.new or Session.identity_map
- upon Session.add(), Session.merge(),
- etc., so that the object is represented
- in these collections when the event
- is called. Added before_attach
- event to accommodate use cases that
- need autoflush w pre-attached object.
-
- .. change::
- :tags: orm, feature
- :tickets:
-
- The Session will produce warnings
- when unsupported methods are used inside the
- "execute" portion of the flush. These are
- the familiar methods add(), delete(), etc.
- as well as collection and related-object
- manipulations, as called within mapper-level
- flush events
- like after_insert(), after_update(), etc.
- It's been prominently documented for a long
- time that SQLAlchemy cannot guarantee
- results when the Session is manipulated within
- the execution of the flush plan,
- however users are still doing it, so now
- there's a warning. Maybe someday the Session
- will be enhanced to support these operations
- inside of the flush, but for now, results
- can't be guaranteed.
-
- .. change::
- :tags: orm, bug
- :tickets: 2582, 2566
-
- Continuing regarding extra
- state post-flush due to event listeners;
- any states that are marked as "dirty" from an
- attribute perspective, usually via column-attribute
- set events within after_insert(), after_update(),
- etc., will get the "history" flag reset
- in all cases, instead of only those instances
- that were part of the flush. This has the effect
- that this "dirty" state doesn't carry over
- after the flush and won't result in UPDATE
- statements. A warning is emitted to this
- effect; the set_committed_state()
- method can be used to assign attributes on objects
- without producing history events.
-
- .. change::
- :tags: orm, feature
- :tickets: 2245
-
- ORM entities can be passed
- to the core select() construct as well
- as to the select_from(),
- correlate(), and correlate_except()
- methods of select(), where they will be unwrapped
- into selectables.
-
- .. change::
- :tags: orm, feature
- :tickets: 2245
-
- Some support for auto-rendering of a
- relationship join condition based on the mapped
- attribute, with usage of core SQL constructs.
- E.g. select([SomeClass]).where(SomeClass.somerelationship)
- would render SELECT from "someclass" and use the
- primaryjoin of "somerelationship" as the WHERE
- clause. This changes the previous meaning
- of "SomeClass.somerelationship" when used in a
- core SQL context; previously, it would "resolve"
- to the parent selectable, which wasn't generally
- useful. Also works with query.filter().
- Related to.
-
- .. change::
- :tags: orm, feature
- :tickets: 2526
-
- The registry of classes
- in declarative_base() is now a
- WeakValueDictionary. So subclasses of
- "Base" that are dereferenced will be
- garbage collected, *if they are not
- referred to by any other mappers/superclass
- mappers*. See the next note for this ticket.
-
- .. change::
- :tags: orm, feature
- :tickets: 2472
-
- Conflicts between columns on
- single-inheritance declarative subclasses,
- with or without using a mixin, can be resolved
- using a new @declared_attr usage described
- in the documentation.
-
- .. change::
- :tags: orm, feature
- :tickets: 2472
-
- declared_attr can now be used
- on non-mixin classes, even though this is generally
- only useful for single-inheritance subclass
- column conflict resolution.
-
- .. change::
- :tags: orm, feature
- :tickets: 2517
-
- declared_attr can now be used with
- attributes that are not Column or MapperProperty;
- including any user-defined value as well
- as association proxy objects.
-
- .. change::
- :tags: orm, bug
- :tickets: 2565
-
- Fixed a disconnect that slowly evolved
- between a @declared_attr Column and a
- directly-defined Column on a mixin. In both
- cases, the Column will be applied to the
- declared class' table, but not to that of a
- joined inheritance subclass. Previously,
- the directly-defined Column would be placed
- on both the base and the sub table, which isn't
- typically what's desired.
-
- .. change::
- :tags: orm, feature
- :tickets: 2526
-
- *Very limited* support for
- inheriting mappers to be GC'ed when the
- class itself is deferenced. The mapper
- must not have its own table (i.e.
- single table inh only) without polymorphic
- attributes in place.
- This allows for the use case of
- creating a temporary subclass of a declarative
- mapped class, with no table or mapping
- directives of its own, to be garbage collected
- when dereferenced by a unit test.
-
- .. change::
- :tags: orm, feature
- :tickets: 2338
-
- Declarative now maintains a registry
- of classes by string name as well as by full
- module-qualified name. Multiple classes with the
- same name can now be looked up based on a module-qualified
- string within relationship(). Simple class name
- lookups where more than one class shares the same
- name now raises an informative error message.
-
- .. change::
- :tags: orm, feature
- :tickets: 2535
-
- Can now provide class-bound attributes
- that override columns which are of any
- non-ORM type, not just descriptors.
-
- .. change::
- :tags: orm, feature
- :tickets: 1729
-
- Added with_labels and
- reduce_columns keyword arguments to
- Query.subquery(), to provide two alternate
- strategies for producing queries with uniquely-
- named columns. .
-
- .. change::
- :tags: orm, feature
- :tickets: 2476
-
- A warning is emitted when a reference
- to an instrumented collection is no longer
- associated with the parent class due to
- expiration/attribute refresh/collection
- replacement, but an append
- or remove operation is received on the
- now-detached collection.
-
- .. change::
- :tags: orm, bug
- :tickets: 2549
-
- Declarative can now propagate a column
- declared on a single-table inheritance subclass
- up to the parent class' table, when the parent
- class is itself mapped to a join() or select()
- statement, directly or via joined inheritance,
- and not just a Table.
-
- .. change::
- :tags: orm, bug
- :tickets:
-
- An error is emitted when uselist=False
- is combined with a "dynamic" loader.
- This is a warning in 0.7.9.
-
- .. change::
- :tags: removed, orm
- :tickets:
-
- Deprecated identifiers removed:
-
- * allow_null_pks mapper() argument
- (use allow_partial_pks)
-
- * _get_col_to_prop() mapper method
- (use get_property_by_column())
-
- * dont_load argument to Session.merge()
- (use load=True)
-
- * sqlalchemy.orm.shard module
- (use sqlalchemy.ext.horizontal_shard)
-
- .. change::
- :tags: engine, feature
- :tickets: 2511
-
- Connection event listeners can
- now be associated with individual
- Connection objects, not just Engine
- objects.
-
- .. change::
- :tags: engine, feature
- :tickets: 2459
-
- The before_cursor_execute event
- fires off for so-called "_cursor_execute"
- events, which are usually special-case
- executions of primary-key bound sequences
- and default-generation SQL
- phrases that invoke separately when RETURNING
- is not used with INSERT.
-
- .. change::
- :tags: engine, feature
- :tickets:
-
- The libraries used by the test suite
- have been moved around a bit so that they are
- part of the SQLAlchemy install again. In addition,
- a new suite of tests is present in the
- new sqlalchemy.testing.suite package. This is
- an under-development system that hopes to provide
- a universal testing suite for external dialects.
- Dialects which are maintained outside of SQLAlchemy
- can use the new test fixture as the framework
- for their own tests, and will get for free a
- "compliance" suite of dialect-focused tests,
- including an improved "requirements" system
- where specific capabilities and features can
- be enabled or disabled for testing.
-
- .. change::
- :tags: engine, bug
- :tickets:
-
- The Inspector.get_table_names()
- order_by="foreign_key" feature now sorts
- tables by dependee first, to be consistent
- with util.sort_tables and metadata.sorted_tables.
-
- .. change::
- :tags: engine, bug
- :tickets: 2522
-
- Fixed bug whereby if a database restart
- affected multiple connections, each
- connection would individually invoke a new
- disposal of the pool, even though only
- one disposal is needed.
-
- .. change::
- :tags: engine, feature
- :tickets: 2462
-
- Added a new system
- for registration of new dialects in-process
- without using an entrypoint. See the
- docs for "Registering New Dialects".
-
- .. change::
- :tags: engine, feature
- :tickets: 2556
-
- The "required" flag is set to
- True by default, if not passed explicitly,
- on bindparam() if the "value" or "callable"
- parameters are not passed.
- This will cause statement execution to check
- for the parameter being present in the final
- collection of bound parameters, rather than
- implicitly assigning None.
-
- .. change::
- :tags: engine, feature
- :tickets:
-
- Various API tweaks to the "dialect"
- API to better support highly specialized
- systems such as the Akiban database, including
- more hooks to allow an execution context to
- access type processors.
-
- .. change::
- :tags: engine, bug
- :tickets: 2397
-
- 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.
-
- .. change::
- :tags: engine, feature
- :tickets: 2422
-
- Inspector.get_primary_keys() is
- deprecated; use Inspector.get_pk_constraint().
- Courtesy Diana Clarke.
-
- .. change::
- :tags: engine, bug
- :tickets:
-
- The autoload_replace flag on Table,
- when False, will cause any reflected foreign key
- constraints which refer to already-declared
- columns to be skipped, assuming that the
- in-Python declared column will take over
- the task of specifying in-Python ForeignKey
- or ForeignKeyConstraint declarations.
-
- .. change::
- :tags: engine, bug
- :tickets: 2498
-
- The ResultProxy methods inserted_primary_key,
- last_updated_params(), last_inserted_params(),
- postfetch_cols(), prefetch_cols() all
- assert that the given statement is a compiled
- construct, and is an insert() or update()
- statement as is appropriate, else
- raise InvalidRequestError.
-
- .. change::
- :tags: engine, feature
- :tickets:
-
- New C extension module "utils" has
- been added for additional function speedups
- as we have time to implement.
-
- .. change::
- :tags: engine
- :tickets:
-
- ResultProxy.last_inserted_ids is removed,
- replaced by inserted_primary_key.
-
- .. change::
- :tags: feature, sql
- :tickets: 2547
-
- Major rework of operator system
- in Core, to allow redefinition of existing
- operators as well as addition of new operators
- at the type level. New types can be created
- from existing ones which add or redefine
- operations that are exported out to column
- expressions, in a similar manner to how the
- ORM has allowed comparator_factory. The new
- architecture moves this capability into the
- Core so that it is consistently usable in
- all cases, propagating cleanly using existing
- type propagation behavior.
-
- .. change::
- :tags: feature, sql
- :tickets: 1534, 2547
-
- To complement, types
- can now provide "bind expressions" and
- "column expressions" which allow compile-time
- injection of SQL expressions into statements
- on a per-column or per-bind level. This is
- to suit the use case of a type which needs
- to augment bind- and result- behavior at the
- SQL level, as opposed to in the Python level.
- Allows for schemes like transparent encryption/
- decryption, usage of PostGIS functions, etc.
-
- .. change::
- :tags: feature, sql
- :tickets:
-
- The Core operator system now includes
- the `getitem` operator, i.e. the bracket
- operator in Python. This is used at first
- to provide index and slice behavior to the
- PostgreSQL ARRAY type, and also provides a hook
- for end-user definition of custom __getitem__
- schemes which can be applied at the type
- level as well as within ORM-level custom
- operator schemes. `lshift` (<<)
- and `rshift` (>>) are also supported as
- optional operators.
-
- Note that this change has the effect that
- descriptor-based __getitem__ schemes used by
- the ORM in conjunction with synonym() or other
- "descriptor-wrapped" schemes will need
- to start using a custom comparator in order
- to maintain this behavior.
-
- .. change::
- :tags: feature, sql
- :tickets: 2537
-
- Revised the rules used to determine
- the operator precedence for the user-defined
- operator, i.e. that granted using the ``op()``
- method. Previously, the smallest precedence
- was applied in all cases, now the default
- precedence is zero, lower than all operators
- except "comma" (such as, used in the argument
- list of a ``func`` call) and "AS", and is
- also customizable via the "precedence" argument
- on the ``op()`` method.
-
- .. change::
- :tags: feature, sql
- :tickets: 2276
-
- Added "collation" parameter to all
- String types. When present, renders as
- COLLATE <collation>. This to support the
- COLLATE keyword now supported by several
- databases including MySQL, SQLite, and PostgreSQL.
-
- .. change::
- :tags: change, sql
- :tickets:
-
- The Text() type renders the length
- given to it, if a length was specified.
-
- .. change::
- :tags: feature, sql
- :tickets:
-
- Custom unary operators can now be
- used by combining operators.custom_op() with
- UnaryExpression().
-
- .. change::
- :tags: bug, sql
- :tickets: 2564
-
- A tweak to column precedence which moves the
- "concat" and "match" operators to be the same as
- that of "is", "like", and others; this helps with
- parenthesization rendering when used in conjunction
- with "IS".
-
- .. change::
- :tags: feature, sql
- :tickets:
-
- Enhanced GenericFunction and func.*
- to allow for user-defined GenericFunction
- subclasses to be available via the func.*
- namespace automatically by classname,
- optionally using a package name, as well
- as with the ability to have the rendered
- name different from the identified name
- in func.*.
-
- .. change::
- :tags: feature, sql
- :tickets: 2562
-
- The cast() and extract() constructs
- will now be produced via the func.* accessor
- as well, as users naturally try to access these
- names from func.* they might as well do
- what's expected, even though the returned
- object is not a FunctionElement.
-
- .. change::
- :tags: changed, sql
- :tickets:
-
- Most classes in expression.sql
- are no longer preceded with an underscore,
- i.e. Label, SelectBase, Generative, CompareMixin.
- _BindParamClause is also renamed to
- BindParameter. The old underscore names for
- these classes will remain available as synonyms
- for the foreseeable future.
-
- .. change::
- :tags: feature, sql
- :tickets: 2208
-
- The Inspector object can now be
- acquired using the new inspect() service,
- part of
-
- .. change::
- :tags: feature, sql
- :tickets: 2418
-
- 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.
-
- .. change::
- :tags: feature, sql
- :tickets: 2423
-
- The behavior of column targeting
- in result sets is now case sensitive by
- default. SQLAlchemy for many years would
- run a case-insensitive conversion on these values,
- probably to alleviate early case sensitivity
- issues with dialects like Oracle and
- Firebird. These issues have been more cleanly
- solved in more modern versions so the performance
- hit of calling lower() on identifiers is removed.
- The case insensitive comparisons can be re-enabled
- by setting "case_insensitive=False" on
- create_engine().
-
- .. change::
- :tags: bug, sql
- :tickets: 2591
-
- Applying a column expression to a select
- statement using a label with or without other
- modifying constructs will no longer "target" that
- expression to the underlying Column; this affects
- ORM operations that rely upon Column targeting
- in order to retrieve results. That is, a query
- like query(User.id, User.id.label('foo')) will now
- track the value of each "User.id" expression separately
- instead of munging them together. It is not expected
- that any users will be impacted by this; however,
- a usage that uses select() in conjunction with
- query.from_statement() and attempts to load fully
- composed ORM entities may not function as expected
- if the select() named Column objects with arbitrary
- .label() names, as these will no longer target to
- the Column objects mapped by that entity.
-
- .. change::
- :tags: feature, sql
- :tickets: 2415
-
- The "unconsumed column names" warning emitted
- when keys are present in insert.values() or update.values()
- that aren't in the target table is now an exception.
-
- .. change::
- :tags: feature, sql
- :tickets: 2502
-
- Added "MATCH" clause to ForeignKey,
- ForeignKeyConstraint, courtesy Ryan Kelly.
-
- .. change::
- :tags: feature, sql
- :tickets: 2507
-
- Added support for DELETE and UPDATE from
- an alias of a table, which would assumedly
- be related to itself elsewhere in the query,
- courtesy Ryan Kelly.
-
- .. change::
- :tags: feature, sql
- :tickets:
-
- select() features a correlate_except()
- method, auto correlates all selectables except those
- passed.
-
- .. change::
- :tags: feature, sql
- :tickets: 2431
-
- The prefix_with() method is now available
- on each of select(), insert(), update(), delete(),
- all with the same API, accepting multiple
- prefix calls, as well as a "dialect name" so that
- the prefix can be limited to one kind of dialect.
-
- .. change::
- :tags: feature, sql
- :tickets: 1729
-
- Added reduce_columns() method
- to select() construct, replaces columns inline
- using the util.reduce_columns utility function
- to remove equivalent columns. reduce_columns()
- also adds "with_only_synonyms" to limit the
- reduction just to those columns which have the same
- name. The deprecated fold_equivalents() feature is
- removed.
-
- .. change::
- :tags: feature, sql
- :tickets: 2470
-
- Reworked the startswith(), endswith(),
- contains() operators to do a better job with
- negation (NOT LIKE), and also to assemble them
- at compilation time so that their rendered SQL
- can be altered, such as in the case for Firebird
- STARTING WITH
-
- .. change::
- :tags: feature, sql
- :tickets: 2463
-
- Added a hook to the system of rendering
- CREATE TABLE that provides access to the render for each
- Column individually, by constructing a @compiles
- function against the new schema.CreateColumn
- construct.
-
- .. change::
- :tags: feature, sql
- :tickets:
-
- "scalar" selects now have a WHERE method
- to help with generative building. Also slight adjustment
- regarding how SS "correlates" columns; the new methodology
- no longer applies meaning to the underlying
- Table column being selected. This improves
- some fairly esoteric situations, and the logic
- that was there didn't seem to have any purpose.
-
- .. change::
- :tags: bug, sql
- :tickets: 2520
-
- Fixes to the interpretation of the
- Column "default" parameter as a callable
- to not pass ExecutionContext into a keyword
- argument parameter.
-
- .. change::
- :tags: bug, sql
- :tickets: 2410
-
- All of UniqueConstraint, ForeignKeyConstraint,
- CheckConstraint, and PrimaryKeyConstraint will
- attach themselves to their parent table automatically
- when they refer to a Table-bound Column object directly
- (i.e. not just string column name), and refer to
- one and only one Table. Prior to 0.8 this behavior
- occurred for UniqueConstraint and PrimaryKeyConstraint,
- but not ForeignKeyConstraint or CheckConstraint.
-
- .. change::
- :tags: bug, sql
- :tickets: 2594
-
- TypeDecorator now includes a generic repr()
- that works in terms of the "impl" type by default.
- This is a behavioral change for those TypeDecorator
- classes that specify a custom __init__ method; those
- types will need to re-define __repr__() if they need
- __repr__() to provide a faithful constructor representation.
-
- .. change::
- :tags: bug, sql
- :tickets: 2168
-
- column.label(None) now produces an
- anonymous label, instead of returning the
- column object itself, consistent with the behavior
- of label(column, None).
-
- .. change::
- :tags: feature, sql
- :tickets: 2455
-
- An explicit error is raised when
- a ForeignKeyConstraint() that was
- constructed to refer to multiple remote tables
- is first used.
-
- .. change::
- :tags: access, feature
- :tickets:
-
- the MS Access dialect has been
- moved to its own project on Bitbucket,
- taking advantage of the new SQLAlchemy
- dialect compliance suite. The dialect is
- still in very rough shape and probably not
- ready for general use yet, however
- it does have *extremely* rudimental
- functionality now.
- https://bitbucket.org/zzzeek/sqlalchemy-access
-
- .. change::
- :tags: maxdb, moved
- :tickets:
-
- The MaxDB dialect, which hasn't been
- functional for several years, is
- moved out to a pending bitbucket project,
- https://bitbucket.org/zzzeek/sqlalchemy-maxdb.
-
- .. change::
- :tags: sqlite, feature
- :tickets: 2363
-
- 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.
-
- .. change::
- :tags: mssql, feature
- :tickets:
-
- SQL Server dialect can be given
- database-qualified schema names,
- i.e. "schema='mydatabase.dbo'"; reflection
- operations will detect this, split the schema
- among the "." to get the owner separately,
- and emit a "USE mydatabase" statement before
- reflecting targets within the "dbo" owner;
- the existing database returned from
- DB_NAME() is then restored.
-
- .. change::
- :tags: mssql, bug
- :tickets: 2277
-
- 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.
-
- .. change::
- :tags: mssql, feature
- :tickets:
-
- updated support for the mxodbc
- driver; mxodbc 3.2.1 is recommended for full
- compatibility.
-
- .. change::
- :tags: postgresql, feature
- :tickets: 2441
-
- postgresql.ARRAY features an optional
- "dimension" argument, will assign a specific
- number of dimensions to the array which will
- render in DDL as ARRAY[][]..., also improves
- performance of bind/result processing.
-
- .. change::
- :tags: postgresql, feature
- :tickets:
-
- postgresql.ARRAY now supports
- indexing and slicing. The Python [] operator
- is available on all SQL expressions that are
- of type ARRAY; integer or simple slices can be
- passed. The slices can also be used on the
- assignment side in the SET clause of an UPDATE
- statement by passing them into Update.values();
- see the docs for examples.
-
- .. change::
- :tags: postgresql, feature
- :tickets:
-
- Added new "array literal" construct
- postgresql.array(). Basically a "tuple" that
- renders as ARRAY[1,2,3].
-
- .. change::
- :tags: postgresql, feature
- :tickets: 2506
-
- Added support for the PostgreSQL ONLY
- keyword, which can appear corresponding to a
- table in a SELECT, UPDATE, or DELETE statement.
- The phrase is established using with_hint().
- Courtesy Ryan Kelly
-
- .. change::
- :tags: postgresql, feature
- :tickets:
-
- The "ischema_names" dictionary of the
- PostgreSQL dialect is "unofficially" customizable.
- Meaning, new types such as PostGIS types can
- be added into this dictionary, and the PG type
- reflection code should be able to handle simple
- types with variable numbers of arguments.
- The functionality here is "unofficial" for
- three reasons:
-
- 1. this is not an "official" API. Ideally
- an "official" API would allow custom type-handling
- callables at the dialect or global level
- in a generic way.
- 2. This is only implemented for the PG dialect,
- in particular because PG has broad support
- for custom types vs. other database backends.
- A real API would be implemented at the
- default dialect level.
- 3. The reflection code here is only tested against
- simple types and probably has issues with more
- compositional types.
-
- patch courtesy Éric Lemoine.
-
- .. change::
- :tags: firebird, feature
- :tickets: 2470
-
- The "startswith()" operator renders
- as "STARTING WITH", "~startswith()" renders
- as "NOT STARTING WITH", using FB's more efficient
- operator.
-
- .. change::
- :tags: firebird, bug
- :tickets: 2505
-
- CompileError is raised when VARCHAR with
- no length is attempted to be emitted, same
- way as MySQL.
-
- .. change::
- :tags: firebird, bug
- :tickets:
-
- Firebird now uses strict "ansi bind rules"
- so that bound parameters don't render in the
- columns clause of a statement - they render
- literally instead.
-
- .. change::
- :tags: firebird, bug
- :tickets:
-
- Support for passing datetime as date when
- using the DateTime type with Firebird; other
- dialects support this.
-
- .. change::
- :tags: firebird, feature
- :tickets: 2504
-
- An experimental dialect for the fdb
- driver is added, but is untested as I cannot
- get the fdb package to build.
-
- .. change::
- :tags: bug, mysql
- :tickets: 2404
-
- Dialect no longer emits expensive server
- collations query, as well as server casing,
- on first connect. These functions are still
- available as semi-private.
-
- .. change::
- :tags: feature, mysql
- :tickets: 2534
-
- Added TIME type to mysql dialect,
- accepts "fst" argument which is the new
- "fractional seconds" specifier for recent
- MySQL versions. The datatype will interpret
- a microseconds portion received from the driver,
- however note that at this time most/all MySQL
- DBAPIs do not support returning this value.
-
- .. change::
- :tags: oracle, bug
- :tickets: 2437
-
- Quoting information is now passed along
- from a Column with quote=True when generating
- a same-named bound parameter to the bindparam()
- object, as is the case in generated INSERT and UPDATE
- statements, so that unknown reserved names can
- be fully supported.
-
- .. change::
- :tags: oracle, feature
- :tickets: 2561
-
- The types of columns excluded from the
- setinputsizes() set can be customized by sending
- a list of string DBAPI type names to exclude,
- using the exclude_setinputsizes dialect parameter.
- This list was previously fixed. The list also
- now defaults to STRING, UNICODE, removing
- CLOB, NCLOB from the list.
-
- .. change::
- :tags: oracle, bug
- :tickets:
-
- The CreateIndex construct in Oracle
- will now schema-qualify the name of the index
- to be that of the parent table. Previously this
- name was omitted which apparently creates the
- index in the default schema, rather than that
- of the table.
-
- .. change::
- :tags: sql, feature
- :tickets: 2580
-
- Added :meth:`.ColumnOperators.notin_`,
- :meth:`.ColumnOperators.notlike`,
- :meth:`.ColumnOperators.notilike` to :class:`.ColumnOperators`.
-
- .. change::
- :tags: sql, removed
-
- The long-deprecated and non-functional ``assert_unicode`` flag on
- :func:`_sa.create_engine` as well as :class:`.String` is removed.
+++ /dev/null
-=============
-0.9 Changelog
-=============
-
-.. changelog_imports::
-
- .. include:: changelog_08.rst
- :start-line: 5
-
-
- .. include:: changelog_07.rst
- :start-line: 5
-
-
-.. _unreleased_changelog::
- :version: 0.9.11
-
- .. change::
- :tags: bug, oracle, py3k
- :tickets: 3491
- :versions: 1.0.9
-
- Fixed support for cx_Oracle version 5.2, which was tripping
- up SQLAlchemy's version detection under Python 3 and inadvertently
- not using the correct unicode mode for Python 3. This would cause
- issues such as bound variables mis-interpreted as NULL and rows
- silently not being returned.
-
- .. change::
- :tags: bug, engine
- :tickets: 3497
- :versions: 1.0.8
-
- Fixed critical issue whereby the pool "checkout" event handler
- may be called against a stale connection without the "connect"
- event handler having been called, in the case where the pool
- attempted to reconnect after being invalidated and failed; the stale
- connection would remain present and would be used on a subsequent
- attempt. This issue has a greater impact in the 1.0 series subsequent
- to 1.0.2, as it also delivers a blanked-out ``.info`` dictionary to
- the event handler; prior to 1.0.2 the ``.info`` dictionary is still
- the previous one.
-
-.. changelog::
- :version: 0.9.10
- :released: July 22, 2015
-
- .. change::
- :tags: bug, sqlite
- :tickets: 3495
- :versions: 1.0.8
-
- Fixed bug in SQLite dialect where reflection of UNIQUE constraints
- that included non-alphabetic characters in the names, like dots or
- spaces, would not be reflected with their name.
-
- .. change::
- :tags: feature, sql
- :tickets: 3418
- :versions: 1.0.5
-
- Added official support for a CTE used by the SELECT present
- inside of :meth:`_expression.Insert.from_select`. This behavior worked
- accidentally up until 0.9.9, when it no longer worked due to
- unrelated changes as part of :ticket:`3248`. Note that this
- is the rendering of the WITH clause after the INSERT, before the
- SELECT; the full functionality of CTEs rendered at the top
- level of INSERT, UPDATE, DELETE is a new feature targeted for a
- later release.
-
- .. change::
- :tags: bug, ext
- :tickets: 3408
- :versions: 1.0.4
-
- Fixed bug where when using extended attribute instrumentation system,
- the correct exception would not be raised when :func:`.class_mapper`
- were called with an invalid input that also happened to not
- be weak referencable, such as an integer.
-
- .. change::
- :tags: bug, tests, pypy
- :tickets: 3406
- :versions: 1.0.4
-
- Fixed an import that prevented "pypy setup.py test" from working
- correctly.
-
- .. change::
- :tags: bug, engine
- :tickets: 3375
- :versions: 1.0.1
-
- Added the string value ``"none"`` to those accepted by the
- :paramref:`_pool.Pool.reset_on_return` parameter as a synonym for ``None``,
- so that string values can be used for all settings, allowing
- utilities like :func:`.engine_from_config` to be usable without
- issue.
-
- .. change::
- :tags: bug, sql
- :tickets: 3362
- :versions: 1.0.0
-
- Fixed issue where a :class:`_schema.MetaData` object that used a naming
- convention would not properly work with pickle. The attribute was
- skipped leading to inconsistencies and failures if the unpickled
- :class:`_schema.MetaData` object were used to base additional tables
- from.
-
- .. change::
- :tags: bug, postgresql
- :tickets: 3354
- :versions: 1.0.0
-
- Fixed a long-standing bug where the :class:`.Enum` type as used
- with the psycopg2 dialect in conjunction with non-ascii values
- and ``native_enum=False`` would fail to decode return results properly.
- This stemmed from when the PG :class:`_postgresql.ENUM` type used
- to be a standalone type without a "non native" option.
-
- .. change::
- :tags: bug, orm
- :tickets: 3349
-
- :class:`_query.Query` doesn't support joins, subselects, or special
- FROM clauses when using the :meth:`_query.Query.update` or
- :meth:`_query.Query.delete` methods; instead of silently ignoring these
- fields if methods like :meth:`_query.Query.join` or
- :meth:`_query.Query.select_from` has been called, a warning is emitted.
- As of 1.0.0b5 this will raise an error.
-
- .. change::
- :tags: bug, orm
- :tickets: 3352
- :versions: 1.0.0b5
-
- Fixed bug where the state tracking within multiple, nested
- :meth:`.Session.begin_nested` operations would fail to propagate
- the "dirty" flag for an object that had been updated within
- the inner savepoint, such that if the enclosing savepoint were
- rolled back, the object would not be part of the state that was
- expired and therefore reverted to its database state.
-
- .. change::
- :tags: bug, mysql, pymysql
- :tickets: 3337
- :versions: 1.0.0b4
-
- Fixed unicode support for PyMySQL when using an "executemany"
- operation with unicode parameters. SQLAlchemy now passes both
- the statement as well as the bound parameters as unicode
- objects, as PyMySQL generally uses string interpolation
- internally to produce the final statement, and in the case of
- executemany does the "encode" step only on the final statement.
-
- .. change::
- :tags: bug, py3k, mysql
- :tickets: 3333
- :versions: 1.0.0b2
-
- Fixed the :class:`.mysql.BIT` type on Py3K which was not using the
- ``ord()`` function correctly. Pull request courtesy David Marin.
-
- .. change::
- :tags: bug, ext
- :tickets: 3324
-
- Fixed regression from 0.9.9 where the :func:`.as_declarative`
- symbol was removed from the ``sqlalchemy.ext.declarative``
- namespace.
-
- .. change::
- :tags: feature, orm
- :tickets: 3320
- :versions: 1.0.0b1
-
- Added a new entry ``"entity"`` to the dictionaries returned by
- :attr:`_query.Query.column_descriptions`. This refers to the primary ORM
- mapped class or aliased class that is referred to by the expression.
- Compared to the existing entry for ``"type"``, it will always be
- a mapped entity, even if extracted from a column expression, or
- None if the given expression is a pure core expression.
- See also :ticket:`3403` which repaired a regression in this feature
- which was unreleased in 0.9.10 but was released in the 1.0 version.
-
-
-.. changelog::
- :version: 0.9.9
- :released: March 10, 2015
-
- .. change::
- :tags: feature, postgresql
- :versions: 1.0.0b1
-
- Added support for the ``CONCURRENTLY`` keyword with PostgreSQL
- indexes, established using ``postgresql_concurrently``. Pull
- request courtesy Iuri de Silvio.
-
- .. seealso::
-
- :ref:`postgresql_index_concurrently`
-
- .. change::
- :tags: bug, ext, py3k
- :versions: 1.0.0b1
-
- Fixed bug where the association proxy list class would not interpret
- slices correctly under Py3K. Pull request courtesy
- Gilles Dartiguelongue.
-
- .. change::
- :tags: feature, sqlite
- :versions: 1.0.0b1
-
- Added support for partial indexes (e.g. with a WHERE clause) on
- SQLite. Pull request courtesy Kai Groner.
-
- .. seealso::
-
- :ref:`sqlite_partial_index`
-
- .. change::
- :tags: bug, orm
- :tickets: 3310
- :versions: 1.0.0b1
-
- Fixed bugs in ORM object comparisons where comparison of
- many-to-one ``!= None`` would fail if the source were an aliased
- class, or if the query needed to apply special aliasing to the
- expression due to aliased joins or polymorphic querying; also fixed
- bug in the case where comparing a many-to-one to an object state
- would fail if the query needed to apply special aliasing
- due to aliased joins or polymorphic querying.
-
- .. change::
- :tags: bug, orm
- :tickets: 3309
- :versions: 1.0.0b1
-
- Fixed bug where internal assertion would fail in the case where
- an ``after_rollback()`` handler for a :class:`.Session` incorrectly
- adds state to that :class:`.Session` within the handler, and the task
- to warn and remove this state (established by :ticket:`2389`) attempts
- to proceed.
-
- .. change::
- :tags: bug, orm
- :versions: 1.0.0b1
-
- Fixed bug where TypeError raised when :meth:`_query.Query.join` called
- with unknown kw arguments would raise its own TypeError due
- to broken formatting. Pull request courtesy Malthe Borch.
-
- .. change::
- :tags: bug, engine
- :tickets: 3302
- :versions: 1.0.0b1
-
- Fixed bug in :class:`_engine.Connection` and pool where the
- :meth:`_engine.Connection.invalidate` method, or an invalidation due
- to a database disconnect, would fail if the
- ``isolation_level`` parameter had been used with
- :meth:`_engine.Connection.execution_options`; the "finalizer" that resets
- the isolation level would be called on the no longer opened connection.
-
- .. change::
- :tags: feature, orm
- :tickets: 3296
- :versions: 1.0.0b1
-
- Added new parameter :paramref:`.Session.connection.execution_options`
- which may be used to set up execution options on a :class:`_engine.Connection`
- when it is first checked out, before the transaction has begun.
- This is used to set up options such as isolation level on the
- connection before the transaction starts.
-
- .. seealso::
-
- :ref:`session_transaction_isolation` - new documentation section
- detailing best practices for setting transaction isolation with
- sessions.
-
- .. change::
- :tags: bug, engine
- :tickets: 3296
- :versions: 1.0.0b1
-
- A warning is emitted if the ``isolation_level`` parameter is used
- with :meth:`_engine.Connection.execution_options` when a :class:`.Transaction`
- is in play; DBAPIs and/or SQLAlchemy dialects such as psycopg2,
- MySQLdb may implicitly rollback or commit the transaction, or
- not change the setting til next transaction, so this is never safe.
-
- .. change::
- :tags: bug, orm
- :tickets: 3300
- :versions: 1.0.0b1
-
- Fixed bug in lazy loading SQL construction whereby a complex
- primaryjoin that referred to the same "local" column multiple
- times in the "column that points to itself" style of self-referential
- join would not be substituted in all cases. The logic to determine
- substitutions here has been reworked to be more open-ended.
-
- .. change::
- :tags: bug, postgresql
- :tickets: 2940
- :versions: 1.0.0b1
-
- Repaired support for PostgreSQL UUID types in conjunction with
- the ARRAY type when using psycopg2. The psycopg2 dialect now
- employs use of the psycopg2.extras.register_uuid() hook
- so that UUID values are always passed to/from the DBAPI as
- UUID() objects. The :paramref:`.UUID.as_uuid` flag is still
- honored, except with psycopg2 we need to convert returned
- UUID objects back into strings when this is disabled.
-
- .. change::
- :tags: bug, postgresql
- :versions: 1.0.0b1
-
- Added support for the :class:`postgresql.JSONB` datatype when
- using psycopg2 2.5.4 or greater, which features native conversion
- of JSONB data so that SQLAlchemy's converters must be disabled;
- additionally, the newly added psycopg2 extension
- ``extras.register_default_jsonb`` is used to establish a JSON
- deserializer passed to the dialect via the ``json_deserializer``
- argument. Also repaired the PostgreSQL integration tests which
- weren't actually round-tripping the JSONB type as opposed to the
- JSON type. Pull request courtesy Mateusz Susik.
-
- .. change::
- :tags: bug, postgresql
- :versions: 1.0.0b1
-
- Repaired the use of the "array_oid" flag when registering the
- HSTORE type with older psycopg2 versions < 2.4.3, which does not
- support this flag, as well as use of the native json serializer
- hook "register_default_json" with user-defined ``json_deserializer``
- on psycopg2 versions < 2.5, which does not include native json.
-
- .. change::
- :tags: bug, schema
- :tickets: 3298, 1765
-
- Fixed bug in 0.9's foreign key setup system, such that
- the logic used to link a :class:`_schema.ForeignKey` to its parent could fail
- when the foreign key used "link_to_name=True" in conjunction with
- a target :class:`_schema.Table` that would not receive its parent column until
- later, such as within a reflection + "useexisting" scenario,
- if the target column in fact had a key value different from its name,
- as would occur in reflection if column reflect events were used to
- alter the .key of reflected :class:`_schema.Column` objects so that the
- link_to_name becomes significant. Also repaired support for column
- type via FK transmission in a similar way when target columns had a
- different key and were referenced using link_to_name.
-
- .. change::
- :tags: feature, engine
- :versions: 1.0.0b1
-
- Added new user-space accessors for viewing transaction isolation
- levels; :meth:`_engine.Connection.get_isolation_level`,
- :attr:`_engine.Connection.default_isolation_level`.
-
- .. change::
- :tags: bug, postgresql
- :versions: 1.0.0b1
- :tickets: 3174
-
- Fixed bug where PostgreSQL dialect would fail to render an
- expression in an :class:`.Index` that did not correspond directly
- to a table-bound column; typically when a :func:`_expression.text` construct
- was one of the expressions within the index; or could misinterpret the
- list of expressions if one or more of them were such an expression.
-
- .. change::
- :tags: bug, orm
- :versions: 1.0.0b1
- :tickets: 3287
-
- The "wildcard" loader options, in particular the one set up by
- the :func:`_orm.load_only` option to cover all attributes not
- explicitly mentioned, now takes into account the superclasses
- of a given entity, if that entity is mapped with inheritance mapping,
- so that attribute names within the superclasses are also omitted
- from the load. Additionally, the polymorphic discriminator column
- is unconditionally included in the list, just in the same way that
- primary key columns are, so that even with load_only() set up,
- polymorphic loading of subtypes continues to function correctly.
-
- .. change::
- :tags: bug, sql
- :versions: 1.0.0b1
-
- Added the ``native_enum`` flag to the ``__repr__()`` output
- of :class:`.Enum`, which is mostly important when using it with
- Alembic autogenerate. Pull request courtesy Dimitris Theodorou.
-
- .. change::
- :tags: bug, orm, pypy
- :versions: 1.0.0b1
- :tickets: 3285
-
- Fixed bug where if an exception were thrown at the start of a
- :class:`_query.Query` before it fetched results, particularly when
- row processors can't be formed, the cursor would stay open with
- results pending and not actually be closed. This is typically only
- an issue on an interpreter like PyPy where the cursor isn't
- immediately GC'ed, and can in some circumstances lead to transactions/
- locks being open longer than is desirable.
-
- .. change::
- :tags: change, mysql
- :versions: 1.0.0b1
- :tickets: 3275
-
- The ``gaerdbms`` dialect is no longer necessary, and emits a
- deprecation warning. Google now recommends using the MySQLdb
- dialect directly.
-
- .. change::
- :tags: bug, sql
- :versions: 1.0.0b1
- :tickets: 3278
-
- Fixed bug where using a :class:`.TypeDecorator` that implemented
- a type that was also a :class:`.TypeDecorator` would fail with
- Python's "Cannot create a consistent method resolution order (MRO)"
- error, when any kind of SQL comparison expression were used against
- an object using this type.
-
- .. change::
- :tags: bug, mysql
- :versions: 1.0.0b1
- :tickets: 3274
-
- Added a version check to the MySQLdb dialect surrounding the
- check for 'utf8_bin' collation, as this fails on MySQL server < 5.0.
-
- .. change::
- :tags: feature, orm
- :versions: 1.0.0b1
-
- Added new method :meth:`.Session.invalidate`, functions similarly
- to :meth:`.Session.close`, except also calls
- :meth:`_engine.Connection.invalidate`
- on all connections, guaranteeing that they will not be returned to
- the connection pool. This is useful in situations e.g. dealing
- with gevent timeouts when it is not safe to use the connection further,
- even for rollbacks.
-
- .. change::
- :tags: bug, examples
- :versions: 1.0.0b1
-
- Updated the :ref:`examples_versioned_history` example such that
- mapped columns are re-mapped to
- match column names as well as grouping of columns; in particular,
- this allows columns that are explicitly grouped in a same-column-named
- joined inheritance scenario to be mapped in the same way in the
- history mappings, avoiding warnings added in the 0.9 series
- regarding this pattern and allowing the same view of attribute
- keys.
-
- .. change::
- :tags: bug, examples
- :versions: 1.0.0b1
-
- Fixed a bug in the examples/generic_associations/discriminator_on_association.py
- example, where the subclasses of AddressAssociation were not being
- mapped as "single table inheritance", leading to problems when trying
- to use the mappings further.
-
- .. change::
- :tags: bug, orm
- :versions: 1.0.0b1
- :tickets: 3251
-
- Fixed a leak which would occur in the unsupported and highly
- non-recommended use case of replacing a relationship on a fixed
- mapped class many times, referring to an arbitrarily growing number of
- target mappers. A warning is emitted when the old relationship is
- replaced, however if the mapping were already used for querying, the
- old relationship would still be referenced within some registries.
-
- .. change::
- :tags: bug, sql
- :versions: 1.0.0b1
- :tickets: 3248
-
- Fixed issue where the columns from a SELECT embedded in an
- INSERT, either through the values clause or as a "from select",
- would pollute the column types used in the result set produced by
- the RETURNING clause when columns from both statements shared the
- same name, leading to potential errors or mis-adaptation when
- retrieving the returning rows.
-
- .. change::
- :tags: bug, orm, sqlite
- :versions: 1.0.0b1
- :tickets: 3241
-
- Fixed bug regarding expression mutations which could express
- itself as a "Could not locate column" error when using
- :class:`_query.Query` to select from multiple, anonymous column
- entities when querying against SQLite, as a side effect of the
- "join rewriting" feature used by the SQLite dialect.
-
- .. change::
- :tags: feature, sqlite
- :versions: 1.0.0b1
-
- Added a new SQLite backend for the SQLCipher backend. This backend
- provides for encrypted SQLite databases using the pysqlcipher Python
- driver, which is very similar to the pysqlite driver.
-
- .. seealso::
-
- :mod:`~sqlalchemy.dialects.sqlite.pysqlcipher`
-
- .. change::
- :tags: bug, orm
- :tickets: 3232
- :versions: 1.0.0b1
-
- Fixed bug where the ON clause for :meth:`_query.Query.join`,
- and :meth:`_query.Query.outerjoin` to a single-inheritance subclass
- using ``of_type()`` would not render the "single table criteria" in
- the ON clause if the ``from_joinpoint=True`` flag were set.
-
-.. changelog::
- :version: 0.9.8
- :released: October 13, 2014
-
- .. change::
- :tags: bug, mysql, mysqlconnector
- :versions: 1.0.0b1
-
- Mysqlconnector as of version 2.0, probably as a side effect of
- the python 3 merge, now does not expect percent signs (e.g.
- as used as the modulus operator and others) to be doubled,
- even when using the "pyformat" bound parameter format (this
- change is not documented by Mysqlconnector). The dialect now
- checks for py2k and for mysqlconnector less than version 2.0
- when detecting if the modulus operator should be rendered as
- ``%%`` or ``%``.
-
- .. change::
- :tags: bug, mysql, mysqlconnector
- :versions: 1.0.0b1
-
- Unicode SQL is now passed for MySQLconnector version 2.0 and above;
- for Py2k and MySQL < 2.0, strings are encoded.
-
-
- .. change::
- :tags: bug, oracle
- :versions: 1.0.0b1
- :tickets: 2138
-
- Fixed long-standing bug in Oracle dialect where bound parameter
- names that started with numbers would not be quoted, as Oracle
- doesn't like numerics in bound parameter names.
-
- .. change::
- :tags: bug, sql
- :versions: 1.0.0b1
- :tickets: 3195
-
- Fixed bug where a fair number of SQL elements within
- the sql package would fail to ``__repr__()`` successfully,
- due to a missing ``description`` attribute that would then invoke
- a recursion overflow when an internal AttributeError would then
- re-invoke ``__repr__()``.
-
- .. change::
- :tags: bug, declarative, orm
- :versions: 1.0.0b1
- :tickets: 3185
-
- Fixed "'NoneType' object has no attribute 'concrete'" error
- when using :class:`.AbstractConcreteBase` in conjunction with
- a subclass that declares ``__abstract__``.
-
- .. change::
- :tags: bug, engine
- :versions: 1.0.0b1
- :tickets: 3200
-
- The execution options passed to an :class:`_engine.Engine` either via
- :paramref:`_sa.create_engine.execution_options` or
- :meth:`_engine.Engine.update_execution_options` are not passed to the
- special :class:`_engine.Connection` used to initialize the dialect
- within the "first connect" event; dialects will usually
- perform their own queries in this phase, and none of the
- current available options should be applied here. In
- particular, the "autocommit" option was causing an attempt to
- autocommit within this initial connect which would fail with
- an AttributeError due to the non-standard state of the
- :class:`_engine.Connection`.
-
- .. change::
- :tags: bug, sqlite
- :versions: 1.0.0b1
- :tickets: 3211
-
- When selecting from a UNION using an attached database file,
- the pysqlite driver reports column names in cursor.description
- as 'dbname.tablename.colname', instead of 'tablename.colname' as
- it normally does for a UNION (note that it's supposed to just be
- 'colname' for both, but we work around it). The column translation
- logic here has been adjusted to retrieve the rightmost token, rather
- than the second token, so it works in both cases. Workaround
- courtesy Tony Roberts.
-
- .. change::
- :tags: bug, postgresql
- :versions: 1.0.0b1
- :tickets: 3021
-
- A revisit to this issue first patched in 0.9.5, apparently
- psycopg2's ``.closed`` accessor is not as reliable as we assumed,
- so we have added an explicit check for the exception messages
- "SSL SYSCALL error: Bad file descriptor" and
- "SSL SYSCALL error: EOF detected" when detecting an
- is-disconnect scenario. We will continue to consult psycopg2's
- connection.closed as a first check.
-
- .. change::
- :tags: bug, orm, engine
- :versions: 1.0.0b1
- :tickets: 3197
-
- Fixed bug that affected generally the same classes of event
- as that of :ticket:`3199`, when the ``named=True`` parameter
- would be used. Some events would fail to register, and others
- would not invoke the event arguments correctly, generally in the
- case of when an event was "wrapped" for adaption in some other way.
- The "named" mechanics have been rearranged to not interfere with
- the argument signature expected by internal wrapper functions.
-
- .. change::
- :tags: bug, declarative
- :versions: 1.0.0b1
- :tickets: 3208
-
- Fixed an unlikely race condition observed in some exotic end-user
- setups, where the attempt to check for "duplicate class name" in
- declarative would hit upon a not-totally-cleaned-up weak reference
- related to some other class being removed; the check here now ensures
- the weakref still references an object before calling upon it further.
-
- .. change::
- :tags: bug, orm
- :versions: 1.0.0b1
- :tickets: 3199
-
- Fixed bug that affected many classes of event, particularly
- ORM events but also engine events, where the usual logic of
- "de duplicating" a redundant call to :func:`.event.listen`
- with the same arguments would fail, for those events where the
- listener function is wrapped. An assertion would be hit within
- registry.py. This assertion has now been integrated into the
- deduplication check, with the added bonus of a simpler means
- of checking deduplication across the board.
-
- .. change::
- :tags: bug, mssql
- :versions: 1.0.0b1
- :tickets: 3151
-
- Fixed the version string detection in the pymssql dialect to
- work with Microsoft SQL Azure, which changes the word "SQL Server"
- to "SQL Azure".
-
- .. change::
- :tags: bug, orm
- :versions: 1.0.0b1
- :tickets: 3194
-
- Fixed warning that would emit when a complex self-referential
- primaryjoin contained functions, while at the same time remote_side
- was specified; the warning would suggest setting "remote side".
- It now only emits if remote_side isn't present.
-
- .. change::
- :tags: bug, ext
- :versions: 1.0.0b1
- :tickets: 3191
-
- Fixed bug in ordering list where the order of items would be
- thrown off during a collection replace event, if the
- reorder_on_append flag were set to True. The fix ensures that the
- ordering list only impacts the list that is explicitly associated
- with the object.
-
- .. change::
- :tags: bug, sql
- :versions: 1.0.0b1
- :tickets: 3180
-
- An adjustment to table/index reflection such that if an index
- reports a column that isn't found to be present in the table,
- a warning is emitted and the column is skipped. This can occur
- for some special system column situations as has been observed
- with Oracle.
-
- .. change::
- :tags: bug, ext
- :versions: 1.0.0b1
-
- Fixed bug where :class:`.ext.mutable.MutableDict`
- failed to implement the ``update()`` dictionary method, thus
- not catching changes. Pull request courtesy Matt Chisholm.
-
- .. change::
- :tags: bug, ext
- :versions: 1.0.0b1
-
- Fixed bug where a custom subclass of :class:`.ext.mutable.MutableDict`
- would not show up in a "coerce" operation, and would instead
- return a plain :class:`.ext.mutable.MutableDict`. Pull request
- courtesy Matt Chisholm.
-
- .. change::
- :tags: bug, pool
- :versions: 1.0.0b1
- :tickets: 3168
-
- Fixed bug in connection pool logging where the "connection checked out"
- debug logging message would not emit if the logging were set up using
- ``logging.setLevel()``, rather than using the ``echo_pool`` flag.
- Tests to assert this logging have been added. This is a
- regression that was introduced in 0.9.0.
-
- .. change::
- :tags: feature, postgresql, pg8000
- :versions: 1.0.0b1
-
- Support is added for "sane multi row count" with the pg8000 driver,
- which applies mostly to when using versioning with the ORM.
- The feature is version-detected based on pg8000 1.9.14 or greater
- in use. Pull request courtesy Tony Locke.
-
- .. change::
- :tags: bug, engine
- :versions: 1.0.0b1
- :tickets: 3165
-
- The string keys that are used to determine the columns impacted
- for an INSERT or UPDATE are now sorted when they contribute towards
- the "compiled cache" cache key. These keys were previously not
- deterministically ordered, meaning the same statement could be
- cached multiple times on equivalent keys, costing both in terms of
- memory as well as performance.
-
- .. change::
- :tags: bug, postgresql
- :versions: 1.0.0b1
- :tickets: 3159
-
- Fixed bug where PostgreSQL JSON type was not able to persist or
- otherwise render a SQL NULL column value, rather than a JSON-encoded
- ``'null'``. To support this case, changes are as follows:
-
- * The value :func:`.null` can now be specified, which will always
- result in a NULL value resulting in the statement.
-
- * A new parameter :paramref:`_types.JSON.none_as_null` is added, which
- when True indicates that the Python ``None`` value should be
- persisted as SQL NULL, rather than JSON-encoded ``'null'``.
-
- Retrieval of NULL as None is also repaired for DBAPIs other than
- psycopg2, namely pg8000.
-
- .. change::
- :tags: bug, sql
- :versions: 1.0.0b1
- :tickets: 3154
-
- Fixed bug in CTE where ``literal_binds`` compiler argument would not
- be always be correctly propagated when one CTE referred to another
- aliased CTE in a statement.
-
- .. change::
- :tags: bug, postgresql
- :versions: 1.0.0b1
- :tickets: 3075
-
- The exception wrapping system for DBAPI errors can now accommodate
- non-standard DBAPI exceptions, such as the psycopg2
- TransactionRollbackError. These exceptions will now be raised
- using the closest available subclass in ``sqlalchemy.exc``, in the
- case of TransactionRollbackError, ``sqlalchemy.exc.OperationalError``.
-
- .. change::
- :tags: bug, sql
- :versions: 1.0.0b1
- :tickets: 3144, 3067
-
- Fixed 0.9.7 regression caused by :ticket:`3067` in conjunction with
- a mis-named unit test such that so-called "schema" types like
- :class:`.Boolean` and :class:`.Enum` could no longer be pickled.
-
- .. change::
- :tags: bug, postgresql
- :versions: 1.0.0b1
- :tickets: 3141
-
- Fixed bug in :class:`_postgresql.array` object where comparison
- to a plain Python list would fail to use the correct array constructor.
- Pull request courtesy Andrew.
-
- .. change::
- :tags: bug, postgresql
- :versions: 1.0.0b1
- :tickets: 3137
-
- Added a supported :meth:`.FunctionElement.alias` method to functions,
- e.g. the ``func`` construct. Previously, behavior for this method
- was undefined. The current behavior mimics that of pre-0.9.4,
- which is that the function is turned into a single-column FROM
- clause with the given alias name, where the column itself is
- anonymously named.
-
-.. changelog::
- :version: 0.9.7
- :released: July 22, 2014
-
- .. change::
- :tags: bug, postgresql, pg8000
- :tickets: 3134
- :versions: 1.0.0b1
-
- Fixed bug introduced in 0.9.5 by new pg8000 isolation level feature
- where engine-level isolation level parameter would raise an error
- on connect.
-
- .. change::
- :tags: bug, oracle, tests
- :tickets: 3128
- :versions: 1.0.0b1
-
- Fixed bug in oracle dialect test suite where in one test,
- 'username' was assumed to be in the database URL, even though
- this might not be the case.
-
- .. change::
- :tags: bug, orm, eagerloading
- :tickets: 3131
- :versions: 1.0.0b1
-
- Fixed a regression caused by :ticket:`2976` released in 0.9.4 where
- the "outer join" propagation along a chain of joined eager loads
- would incorrectly convert an "inner join" along a sibling join path
- into an outer join as well, when only descendant paths should be
- receiving the "outer join" propagation; additionally, fixed related
- issue where "nested" join propagation would take place inappropriately
- between two sibling join paths.
-
- .. change::
- :tags: bug, sqlite
- :tickets: 3130
- :versions: 1.0.0b1
-
- Fixed a SQLite join rewriting issue where a subquery that is embedded
- as a scalar subquery such as within an IN would receive inappropriate
- substitutions from the enclosing query, if the same table were present
- inside the subquery as were in the enclosing query such as in a
- joined inheritance scenario.
-
- .. change::
- :tags: bug, sql
- :tickets: 3067
- :versions: 1.0.0b1
-
- Fix bug in naming convention feature where using a check
- constraint convention that includes ``constraint_name`` would
- then force all :class:`.Boolean` and :class:`.Enum` types to
- require names as well, as these implicitly create a
- constraint, even if the ultimate target backend were one that does
- not require generation of the constraint such as PostgreSQL.
- The mechanics of naming conventions for these particular
- constraints has been reorganized such that the naming
- determination is done at DDL compile time, rather than at
- constraint/table construction time.
-
- .. change::
- :tags: bug, mssql
- :tickets: 3025
-
- Fixed a regression from 0.9.5 caused by :ticket:`3025` where the
- query used to determine "default schema" is invalid in SQL Server 2000.
- For SQL Server 2000 we go back to defaulting to the "schema name"
- parameter of the dialect, which is configurable but defaults
- to 'dbo'.
-
- .. change::
- :tags: bug, orm
- :tickets: 3083, 2736
- :versions: 1.0.0b1
-
- Fixed a regression from 0.9.0 due to :ticket:`2736` where the
- :meth:`_query.Query.select_from` method no longer set up the "from
- entity" of the :class:`_query.Query` object correctly, so that
- subsequent :meth:`_query.Query.filter_by` or :meth:`_query.Query.join`
- calls would fail to check the appropriate "from" entity when
- searching for attributes by string name.
-
- .. change::
- :tags: bug, sql
- :tickets: 3090
- :versions: 1.0.0b1
-
- Fixed bug in common table expressions whereby positional bound
- parameters could be expressed in the wrong final order
- when CTEs were nested in certain ways.
-
- .. change::
- :tags: bug, sql
- :tickets: 3069
- :versions: 1.0.0b1
-
- Fixed bug where multi-valued :class:`_expression.Insert` construct would fail
- to check subsequent values entries beyond the first one given
- for literal SQL expressions.
-
- .. change::
- :tags: bug, sql
- :tickets: 3123
- :versions: 1.0.0b1
-
- Added a "str()" step to the dialect_kwargs iteration for
- Python version < 2.6.5, working around the
- "no unicode keyword arg" bug as these args are passed along as
- keyword args within some reflection processes.
-
- .. change::
- :tags: bug, sql
- :tickets: 3122
- :versions: 1.0.0b1
-
- The :meth:`.TypeEngine.with_variant` method will now accept a
- type class as an argument which is internally converted to an
- instance, using the same convention long established by other
- constructs such as :class:`_schema.Column`.
-
- .. change::
- :tags: bug, orm
- :tickets: 3117
-
- The "evaluator" for query.update()/delete() won't work with multi-table
- updates, and needs to be set to `synchronize_session=False` or
- `synchronize_session='fetch'`; a warning is now emitted. In
- 1.0 this will be promoted to a full exception.
-
- .. change::
- :tags: bug, tests
- :versions: 1.0.0b1
-
- Fixed bug where "python setup.py test" wasn't calling into
- distutils appropriately, and errors would be emitted at the end
- of the test suite.
-
- .. change::
- :tags: feature, postgresql
- :versions: 1.0.0b1
- :tickets: 3078
-
- Added kw argument ``postgresql_regconfig`` to the
- :meth:`.ColumnOperators.match` operator, allows the "reg config" argument
- to be specified to the ``to_tsquery()`` function emitted.
- Pull request courtesy Jonathan Vanasco.
-
- .. change::
- :tags: feature, postgresql
- :versions: 1.0.0b1
-
- Added support for PostgreSQL JSONB via :class:`_postgresql.JSONB`. Pull request
- courtesy Damian Dimmich.
-
- .. change::
- :tags: feature, mssql
- :versions: 1.0.0b1
-
- Enabled "multivalues insert" for SQL Server 2008. Pull request
- courtesy Albert Cervin. Also expanded the checks for "IDENTITY INSERT"
- mode to include when the identity key is present in the
- VALUEs clause of the statement.
-
- .. change::
- :tags: feature, engine
- :tickets: 3076
- :versions: 1.0.0b1
-
- Added new event :meth:`_events.ConnectionEvents.handle_error`, a more
- fully featured and comprehensive replacement for
- :meth:`_events.ConnectionEvents.dbapi_error`.
-
- .. change::
- :tags: bug, orm
- :tickets: 3108
- :versions: 1.0.0b1
-
- Fixed bug where items that were persisted, deleted, or had a
- primary key change within a savepoint block would not
- participate in being restored to their former state (not in
- session, in session, previous PK) after the outer transaction
- were rolled back.
-
- .. change::
- :tags: bug, orm
- :tickets: 3106
- :versions: 1.0.0b1
-
- Fixed bug in subquery eager loading in conjunction with
- :func:`.with_polymorphic`, the targeting of entities and columns
- in the subquery load has been made more accurate with respect
- to this type of entity and others.
-
- .. change::
- :tags: bug, orm
- :tickets: 3099
-
- Fixed bug involving dynamic attributes, that was again a regression
- of :ticket:`3060` from version 0.9.5. A self-referential relationship
- with lazy='dynamic' would raise a TypeError within a flush operation.
-
- .. change::
- :tags: bug, declarative
- :tickets: 3097
- :versions: 1.0.0b1
-
- Fixed bug when the declarative ``__abstract__`` flag was not being
- distinguished for when it was actually the value ``False``.
- The ``__abstract__`` flag needs to actually evaluate to a True
- value at the level being tested.
-
-.. changelog::
- :version: 0.9.6
- :released: June 23, 2014
-
- .. change::
- :tags: bug, orm
- :tickets: 3060
-
- Reverted the change for :ticket:`3060` - this is a unit of work
- fix that is updated more comprehensively in 1.0 via :ticket:`3061`.
- The fix in :ticket:`3060` unfortunately produces a new issue whereby
- an eager load of a many-to-one attribute can produce an event
- that is interpreted into an attribute change.
-
-.. changelog::
- :version: 0.9.5
- :released: June 23, 2014
-
- .. change::
- :tags: bug, orm
- :tickets: 3042
- :versions: 1.0.0b1
-
- Additional checks have been added for the case where an inheriting
- mapper is implicitly combining one of its column-based attributes
- with that of the parent, where those columns normally don't necessarily
- share the same value. This is an extension of an existing check that
- was added via :ticket:`1892`; however this new check emits only a
- warning, instead of an exception, to allow for applications that may
- be relying upon the existing behavior.
-
- .. seealso::
-
- :ref:`faq_combining_columns`
-
- .. change::
- :tags: bug, sql
- :tickets: 3023
- :versions: 1.0.0b1
-
- The :paramref:`_schema.Column.nullable` flag is implicitly set to ``False``
- when that :class:`_schema.Column` is referred to in an explicit
- :class:`.PrimaryKeyConstraint` for that table. This behavior now
- matches that of when the :class:`_schema.Column` itself has the
- :paramref:`_schema.Column.primary_key` flag set to ``True``, which is
- intended to be an exactly equivalent case.
-
- .. change::
- :tags: enhancement, postgresql
- :tickets: 3002
- :versions: 1.0.0b1
-
- Added a new type :class:`_postgresql.OID` to the PostgreSQL dialect.
- While "oid" is generally a private type within PG that is not exposed
- in modern versions, there are some PG use cases such as large object
- support where these types might be exposed, as well as within some
- user-reported schema reflection use cases.
-
- .. change::
- :tags: bug, orm
- :tickets: 3080
- :versions: 1.0.0b1
-
- Modified the behavior of :func:`_orm.load_only` such that primary key
- columns are always added to the list of columns to be "undeferred";
- otherwise, the ORM can't load the row's identity. Apparently,
- one can defer the mapped primary keys and the ORM will fail, that
- hasn't been changed. But as load_only is essentially saying
- "defer all but X", it's more critical that PK cols not be part of this
- deferral.
-
- .. change::
- :tags: feature, examples
- :versions: 1.0.0b1
-
- Added a new example illustrating materialized paths, using the
- latest relationship features. Example courtesy Jack Zhou.
-
- .. change::
- :tags: bug, testsuite
- :versions: 1.0.0b1
-
- In public test suite, changed to use of ``String(40)`` from
- less-supported ``Text`` in ``StringTest.test_literal_backslashes``.
- Pullreq courtesy Jan.
-
- .. change::
- :tags: bug, engine
- :versions: 1.0.0b1
- :tickets: 3063
-
- Fixed bug which would occur if a DBAPI exception
- occurs when the engine first connects and does its initial checks,
- and the exception is not a disconnect exception, yet the cursor
- raises an error when we try to close it. In this case the real
- exception would be quashed as we tried to log the cursor close
- exception via the connection pool and failed, as we were trying
- to access the pool's logger in a way that is inappropriate
- in this very specific scenario.
-
- .. change::
- :tags: feature, postgresql
- :versions: 1.0.0b1
-
- Added support for AUTOCOMMIT isolation level when using the pg8000
- DBAPI. Pull request courtesy Tony Locke.
-
- .. change::
- :tags: bug, postgresql
- :tickets: 3021
- :versions: 1.0.0b1
-
- The psycopg2 ``.closed`` accessor is now consulted when determining
- if an exception is a "disconnect" error; ideally, this should remove
- the need for any other inspection of the exception message to detect
- disconnect, however we will leave those existing messages in place
- as a fallback. This should be able to handle newer cases like
- "SSL EOF" conditions. Pull request courtesy Dirk Mueller.
-
- .. change::
- :tags: bug, orm
- :tickets: 3060
- :versions: 1.0.0b1
-
- Fixed a few edge cases which arise in the so-called "row switch"
- scenario, where an INSERT/DELETE can be turned into an UPDATE.
- In this situation, a many-to-one relationship set to None, or
- in some cases a scalar attribute set to None, may not be detected
- as a net change in value, and therefore the UPDATE would not reset
- what was on the previous row. This is due to some as-yet
- unresolved side effects of the way attribute history works in terms
- of implicitly assuming None isn't really a "change" for a previously
- un-set attribute. See also :ticket:`3061`.
-
- .. note::
-
- This change has been **REVERTED** in 0.9.6. The full fix
- will be in version 1.0 of SQLAlchemy.
-
-
- .. change::
- :tags: bug, orm
- :versions: 1.0.0b1
-
- Related to :ticket:`3060`, an adjustment has been made to the unit
- of work such that loading for related many-to-one objects is slightly
- more aggressive, in the case of a graph of self-referential objects
- that are to be deleted; the load of related objects is to help
- determine the correct order for deletion if passive_deletes is
- not set.
-
- .. change::
- :tags: bug, orm
- :tickets: 3057
- :versions: 1.0.0b1
-
- Fixed bug in SQLite join rewriting where anonymized column names
- due to repeats would not correctly be rewritten in subqueries.
- This would affect SELECT queries with any kind of subquery + join.
-
- .. change::
- :tags: bug, sql
- :tickets: 3012
- :versions: 1.0.0b1
-
- Fixed bug where the :meth:`.Operators.__and__`,
- :meth:`.Operators.__or__` and :meth:`.Operators.__invert__`
- operator overload methods could not be overridden within a custom
- :class:`.TypeEngine.Comparator` implementation.
-
- .. change::
- :tags: feature, postgresql
- :tickets: 2785
- :versions: 1.0.0b1
-
- Added a new flag :paramref:`_types.ARRAY.zero_indexes` to the PostgreSQL
- :class:`_types.ARRAY` type. When set to ``True``, a value of one will be
- added to all array index values before passing to the database, allowing
- better interoperability between Python style zero-based indexes and
- PostgreSQL one-based indexes. Pull request courtesy Alexey Terentev.
-
- .. change::
- :tags: bug, engine
- :tickets: 3043
- :versions: 1.0.0b1
-
- Fixed some "double invalidate" situations were detected where
- a connection invalidation could occur within an already critical section
- like a connection.close(); ultimately, these conditions are caused
- by the change in :ticket:`2907`, in that the "reset on return" feature
- calls out to the Connection/Transaction in order to handle it, where
- "disconnect detection" might be caught. However, it's possible that
- the more recent change in :ticket:`2985` made it more likely for this
- to be seen as the "connection invalidate" operation is much quicker,
- as the issue is more reproducible on 0.9.4 than 0.9.3.
-
- Checks are now added within any section that
- an invalidate might occur to halt further disallowed operations
- on the invalidated connection. This includes two fixes both at the
- engine level and at the pool level. While the issue was observed
- with highly concurrent gevent cases, it could in theory occur in
- any kind of scenario where a disconnect occurs within the connection
- close operation.
-
- .. change::
- :tags: feature, orm
- :tickets: 3029
- :versions: 1.0.0b1
-
- The "primaryjoin" model has been stretched a bit further to allow
- a join condition that is strictly from a single column to itself,
- translated through some kind of SQL function or expression. This
- is kind of experimental, but the first proof of concept is a
- "materialized path" join condition where a path string is compared
- to itself using "like". The :meth:`.ColumnOperators.like` operator has
- also been added to the list of valid operators to use in a primaryjoin
- condition.
-
- .. change::
- :tags: feature, sql
- :tickets: 3028
- :versions: 1.0.0b1
-
- Liberalized the contract for :class:`.Index` a bit in that you can
- specify a :func:`_expression.text` expression as the target; the index no longer
- needs to have a table-bound column present if the index is to be
- manually added to the table, either via inline declaration or via
- :meth:`_schema.Table.append_constraint`.
-
- .. change::
- :tags: bug, firebird
- :tickets: 3038
-
- Fixed bug where the combination of "limit" rendering as
- "SELECT FIRST n ROWS" using a bound parameter (only firebird has both),
- combined with column-level subqueries
- which also feature "limit" as well as "positional" bound parameters
- (e.g. qmark style) would erroneously assign the subquery-level positions
- before that of the enclosing SELECT, thus returning parameters which
- are out of order.
-
- .. change::
- :tags: bug, mssql
- :tickets: 3025
- :versions: 1.0.0b1
-
- Revised the query used to determine the current default schema name
- to use the ``database_principal_id()`` function in conjunction with
- the ``sys.database_principals`` view so that we can determine
- the default schema independently of the type of login in progress
- (e.g., SQL Server, Windows, etc).
-
- .. change::
- :tags: bug, sql
- :tickets: 3024
- :versions: 1.0.0b1
-
- Fixed bug in new :meth:`.DialectKWArgs.argument_for` method where
- adding an argument for a construct not previously included for any
- special arguments would fail.
-
- .. change::
- :tags: bug, py3k, tests
- :tickets: 2830
- :versions: 1.0.0b1
-
- Corrected for some deprecation warnings involving the ``imp``
- module and Python 3.3 or greater, when running tests. Pull
- request courtesy Matt Chisholm.
-
- .. change::
- :tags: bug, sql
- :tickets: 3020, 1068
- :versions: 1.0.0b1
-
- Fixed regression introduced in 0.9 where new "ORDER BY <labelname>"
- feature from :ticket:`1068` would not apply quoting rules to the
- label name as rendered in the ORDER BY.
-
- .. change::
- :tags: feature, orm
- :tickets: 3017
- :versions: 1.0.0b1
-
- Added new utility function :func:`.make_transient_to_detached` which can
- be used to manufacture objects that behave as though they were loaded
- from a session, then detached. Attributes that aren't present
- are marked as expired, and the object can be added to a Session
- where it will act like a persistent one.
-
- .. change::
- :tags: bug, sql
- :versions: 1.0.0b1
-
- Restored the import for :class:`.Function` to the ``sqlalchemy.sql.expression``
- import namespace, which was removed at the beginning of 0.9.
-
- .. change::
- :tags: bug, orm, sql
- :tickets: 3013
- :versions: 1.0.0b1
-
- Fixes to the newly enhanced boolean coercion in :ticket:`2804` where
- the new rules for "where" and "having" wouldn't take effect for the
- "whereclause" and "having" kw arguments of the :func:`_expression.select` construct,
- which is also what :class:`_query.Query` uses so wasn't working in the
- ORM either.
-
- .. change::
- :tags: feature, sql
- :tickets: 2990
- :versions: 1.0.0b1
-
- Added new flag :paramref:`.expression.between.symmetric`, when set to True
- renders "BETWEEN SYMMETRIC". Also added a new negation operator
- "notbetween_op", which now allows an expression like ``~col.between(x, y)``
- to render as "col NOT BETWEEN x AND y", rather than a parenthesized NOT
- string.
-
-.. changelog::
- :version: 0.9.4
- :released: March 28, 2014
-
- .. change::
- :tags: feature, orm
- :tickets: 3007
-
- Added new parameter :paramref:`.orm.mapper.confirm_deleted_rows`. Defaults
- to True, indicates that a series of DELETE statements should confirm
- that the cursor rowcount matches the number of primary keys that should
- have matched; this behavior had been taken off in most cases
- (except when version_id is used) to support the unusual edge case of
- self-referential ON DELETE CASCADE; to accommodate this, the message
- is now just a warning, not an exception, and the flag can be used
- to indicate a mapping that expects self-referential cascaded
- deletes of this nature. See also :ticket:`2403` for background on the
- original change.
-
- .. change::
- :tags: bug, ext, automap
- :tickets: 3004
-
- Added support to automap for the case where a relationship should
- not be created between two classes that are in a joined inheritance
- relationship, for those foreign keys that link the subclass back to
- the superclass.
-
- .. change::
- :tags: bug, orm
- :tickets: 2948
-
- Fixed a very old behavior where the lazy load emitted for a one-to-many
- could inappropriately pull in the parent table, and also return results
- inconsistent based on what's in the parent table, when the primaryjoin
- includes some kind of discriminator against the parent table, such
- as ``and_(parent.id == child.parent_id, parent.deleted == False)``.
- While this primaryjoin doesn't make that much sense for a one-to-many,
- it is slightly more common when applied to the many-to-one side, and
- the one-to-many comes as a result of a backref.
- Loading rows from ``child`` in this case would keep ``parent.deleted == False``
- as is within the query, thereby yanking it into the FROM clause
- and doing a cartesian product. The new behavior will now substitute
- the value of the local "parent.deleted" for that parameter as is
- appropriate. Though typically, a real-world app probably wants to use a
- different primaryjoin for the o2m side in any case.
-
- .. change::
- :tags: bug, orm
- :tickets: 2965
-
- Improved the check for "how to join from A to B" such that when
- a table has multiple, composite foreign keys targeting a parent table,
- the :paramref:`_orm.relationship.foreign_keys` argument will be properly
- interpreted in order to resolve the ambiguity; previously this condition
- would raise that there were multiple FK paths when in fact the
- foreign_keys argument should be establishing which one is expected.
-
- .. change::
- :tags: bug, mysql
-
- Tweaked the settings for mysql-connector-python; in Py2K, the
- "supports unicode statements" flag is now False, so that SQLAlchemy
- will encode the *SQL string* (note: *not* the parameters)
- to bytes before sending to the database. This seems to allow
- all unicode-related tests to pass for mysql-connector, including those
- that use non-ascii table/column names, as well as some tests for the
- TEXT type using unicode under cursor.executemany().
-
- .. change::
- :tags: feature, engine
-
- Added some new event mechanics for dialect-level events; the initial
- implementation allows an event handler to redefine the specific mechanics
- by which an arbitrary dialect invokes execute() or executemany() on a
- DBAPI cursor. The new events, at this point semi-public and experimental,
- are in support of some upcoming transaction-related extensions.
-
- .. change::
- :tags: feature, engine
- :tickets: 2978
-
- An event listener can now be associated with a :class:`_engine.Engine`,
- after one or more :class:`_engine.Connection` objects have been created
- (such as by an orm :class:`.Session` or via explicit connect)
- and the listener will pick up events from those connections.
- Previously, performance concerns pushed the event transfer from
- :class:`_engine.Engine` to :class:`_engine.Connection` at init-time only, but
- we've inlined a bunch of conditional checks to make this possible
- without any additional function calls.
-
- .. change::
- :tags: bug, tests
- :tickets: 2980
-
- Fixed a few errant ``u''`` strings that would prevent tests from passing
- in Py3.2. Patch courtesy Arfrever Frehtes Taifersar Arahesis.
-
- .. change::
- :tags: bug, engine
- :tickets: 2985
-
- A major improvement made to the mechanics by which the :class:`_engine.Engine`
- recycles the connection pool when a "disconnect" condition is detected;
- instead of discarding the pool and explicitly closing out connections,
- the pool is retained and a "generational" timestamp is updated to
- reflect the current time, thereby causing all existing connections
- to be recycled when they are next checked out. This greatly simplifies
- the recycle process, removes the need for "waking up" connect attempts
- waiting on the old pool and eliminates the race condition that many
- immediately-discarded "pool" objects could be created during the
- recycle operation.
-
- .. change::
- :tags: bug, oracle
- :tickets: 2987
-
- Added new datatype :class:`_oracle.DATE`, which is a subclass of
- :class:`.DateTime`. As Oracle has no "datetime" type per se,
- it instead has only ``DATE``, it is appropriate here that the
- ``DATE`` type as present in the Oracle dialect be an instance of
- :class:`.DateTime`. This issue doesn't change anything as far as
- the behavior of the type, as data conversion is handled by the
- DBAPI in any case, however the improved subclass layout will help
- the use cases of inspecting types for cross-database compatibility.
- Also removed uppercase ``DATETIME`` from the Oracle dialect as this
- type isn't functional in that context.
-
- .. change::
- :tags: bug, sql
- :tickets: 2988
-
- Fixed an 0.9 regression where a :class:`_schema.Table` that failed to
- reflect correctly wouldn't be removed from the parent
- :class:`_schema.MetaData`, even though in an invalid state. Pullreq
- courtesy Roman Podoliaka.
-
- .. change::
- :tags: bug, engine
-
- The :meth:`_events.ConnectionEvents.after_cursor_execute` event is now
- emitted for the "_cursor_execute()" method of :class:`_engine.Connection`;
- this is the "quick" executor that is used for things like
- when a sequence is executed ahead of an INSERT statement, as well as
- for dialect startup checks like unicode returns, charset, etc.
- the :meth:`_events.ConnectionEvents.before_cursor_execute` event was already
- invoked here. The "executemany" flag is now always set to False
- here, as this event always corresponds to a single execution.
- Previously the flag could be True if we were acting on behalf of
- an executemany INSERT statement.
-
- .. change::
- :tags: bug, orm
-
- Added support for the not-quite-yet-documented ``insert=True``
- flag for :func:`.event.listen` to work with mapper / instance events.
-
- .. change::
- :tags: feature, sql
-
- Added support for literal rendering of boolean values, e.g.
- "true" / "false" or "1" / "0".
-
- .. change::
- :tags: feature, sql
-
- Added a new feature :func:`_schema.conv`, the purpose of which is to
- mark a constraint name as already having had a naming convention applied.
- This token will be used by Alembic migrations as of Alembic 0.6.4
- in order to render constraints in migration scripts with names marked
- as already having been subject to a naming convention.
-
- .. change::
- :tags: bug, sql
-
- :paramref:`_schema.MetaData.naming_convention` feature will now also
- apply to :class:`.CheckConstraint` objects that are associated
- directly with a :class:`_schema.Column` instead of just on the
- :class:`_schema.Table`.
-
- .. change::
- :tags: bug, sql
- :tickets: 2991
-
- Fixed bug in new :paramref:`_schema.MetaData.naming_convention` feature
- where the name of a check constraint making use of the
- `"%(constraint_name)s"` token would get doubled up for the
- constraint generated by a boolean or enum type, and overall
- duplicate events would cause the `"%(constraint_name)s"` token
- to keep compounding itself.
-
- .. change::
- :tags: feature, orm
-
- A warning is emitted if the :meth:`.MapperEvents.before_configured`
- or :meth:`.MapperEvents.after_configured` events are applied to a
- specific mapper or mapped class, as the events are only invoked
- for the :class:`_orm.Mapper` target at the general level.
-
- .. change::
- :tags: feature, orm
-
- Added a new keyword argument ``once=True`` to :func:`.event.listen`
- and :func:`.event.listens_for`. This is a convenience feature which
- will wrap the given listener such that it is only invoked once.
-
- .. change::
- :tags: feature, oracle
- :tickets: 2911
-
- Added a new engine option ``coerce_to_unicode=True`` to the
- cx_Oracle dialect, which restores the cx_Oracle outputtypehandler
- approach to Python unicode conversion under Python 2, which was
- removed in 0.9.2 as a result of :ticket:`2911`. Some use cases would
- prefer that unicode coercion is unconditional for all string values,
- despite performance concerns. Pull request courtesy
- Christoph Zwerschke.
-
- .. change::
- :tags: bug, pool
-
- Fixed small issue in :class:`.SingletonThreadPool` where the current
- connection to be returned might get inadvertently cleaned out during
- the "cleanup" process. Patch courtesy jd23.
-
- .. change::
- :tags: bug, ext, py3k
-
- Fixed bug in association proxy where assigning an empty slice
- (e.g. ``x[:] = [...]``) would fail on Py3k.
-
- .. change::
- :tags: bug, general
- :tickets: 2979
-
- Fixed some test/feature failures occurring in Python 3.4,
- in particular the logic used to wrap "column default" callables
- wouldn't work properly for Python built-ins.
-
- .. change::
- :tags: feature, general
-
- Support has been added for pytest to run tests. This runner
- is currently being supported in addition to nose, and will likely
- be preferred to nose going forward. The nose plugin system used
- by SQLAlchemy has been split out so that it works under pytest as
- well. There are no plans to drop support for nose at the moment
- and we hope that the test suite itself can continue to remain as
- agnostic of testing platform as possible. See the file
- README.unittests.rst for updated information on running tests
- with pytest.
-
- The test plugin system has also been enhanced to support running
- tests against multiple database URLs at once, by specifying the ``--db``
- and/or ``--dburi`` flags multiple times. This does not run the entire test
- suite for each database, but instead allows test cases that are specific
- to certain backends make use of that backend as the test is run.
- When using pytest as the test runner, the system will also run
- specific test suites multiple times, once for each database, particularly
- those tests within the "dialect suite". The plan is that the enhanced
- system will also be used by Alembic, and allow Alembic to run
- migration operation tests against multiple backends in one run, including
- third-party backends not included within Alembic itself.
- Third party dialects and extensions are also encouraged to standardize
- on SQLAlchemy's test suite as a basis; see the file README.dialects.rst
- for background on building out from SQLAlchemy's test platform.
-
- .. change::
- :tags: feature, orm
- :tickets: 2976
-
- Added a new option to :paramref:`_orm.relationship.innerjoin` which is
- to specify the string ``"nested"``. When set to ``"nested"`` as opposed
- to ``True``, the "chaining" of joins will parenthesize the inner join on the
- right side of an existing outer join, instead of chaining as a string
- of outer joins. This possibly should have been the default behavior
- when 0.9 was released, as we introduced the feature of right-nested
- joins in the ORM, however we are keeping it as a non-default for now
- to avoid further surprises.
-
- .. seealso::
-
- :ref:`feature_2976`
-
- .. change::
- :tags: bug, ext
- :tickets: 2810
-
- Fixed a regression in association proxy caused by :ticket:`2810` which
- caused a user-provided "getter" to no longer receive values of ``None``
- when fetching scalar values from a target that is non-present. The
- check for None introduced by this change is now moved into the default
- getter, so a user-provided getter will also again receive values of
- None.
-
- .. change::
- :tags: bug, sql
- :tickets: 2974
-
- Adjusted the logic which applies names to the .c collection when
- a no-name :class:`.BindParameter` is received, e.g. via :func:`_expression.literal`
- or similar; the "key" of the bind param is used as the key within
- .c. rather than the rendered name. Since these binds have "anonymous"
- names in any case, this allows individual bound parameters to
- have their own name within a selectable if they are otherwise unlabeled.
-
- .. change::
- :tags: bug, sql
- :tickets: 2974
-
- Some changes to how the :attr:`_expression.FromClause.c` collection behaves
- when presented with duplicate columns. The behavior of emitting a
- warning and replacing the old column with the same name still
- remains to some degree; the replacement in particular is to maintain
- backwards compatibility. However, the replaced column still remains
- associated with the ``c`` collection now in a collection ``._all_columns``,
- which is used by constructs such as aliases and unions, to deal with
- the set of columns in ``c`` more towards what is actually in the
- list of columns rather than the unique set of key names. This helps
- with situations where SELECT statements with same-named columns
- are used in unions and such, so that the union can match the columns
- up positionally and also there's some chance of :meth:`_expression.FromClause.corresponding_column`
- still being usable here (it can now return a column that is only
- in selectable.c._all_columns and not otherwise named).
- The new collection is underscored as we still need to decide where this
- list might end up. Theoretically it
- would become the result of iter(selectable.c), however this would mean
- that the length of the iteration would no longer match the length of
- keys(), and that behavior needs to be checked out.
-
- .. change::
- :tags: bug, sql
-
- Fixed issue in new :meth:`_expression.TextClause.columns` method where the ordering
- of columns given positionally would not be preserved. This could
- have potential impact in positional situations such as applying the
- resulting :class:`.TextAsFrom` object to a union.
-
- .. change::
- :tags: feature, sql
- :tickets: 2962, 2866
-
- The new dialect-level keyword argument system for schema-level
- constructs has been enhanced in order to assist with existing
- schemes that rely upon addition of ad-hoc keyword arguments to
- constructs.
-
- E.g., a construct such as :class:`.Index` will again accept
- ad-hoc keyword arguments within the :attr:`.Index.kwargs` collection,
- after construction::
-
- idx = Index("a", "b")
- idx.kwargs["mysql_someargument"] = True
-
- To suit the use case of allowing custom arguments at construction time,
- the :meth:`.DialectKWArgs.argument_for` method now allows this registration::
-
- Index.argument_for("mysql", "someargument", False)
-
- idx = Index("a", "b", mysql_someargument=True)
-
- .. seealso::
-
- :meth:`.DialectKWArgs.argument_for`
-
- .. change::
- :tags: bug, orm, engine
- :tickets: 2973
-
- Fixed bug where events set to listen at the class
- level (e.g. on the :class:`_orm.Mapper` or :class:`.ClassManager`
- level, as opposed to on an individual mapped class, and also on
- :class:`_engine.Connection`) that also made use of internal argument conversion
- (which is most within those categories) would fail to be removable.
-
- .. change::
- :tags: bug, orm
-
- Fixed regression from 0.8 where using an option like
- :func:`_orm.lazyload` with the "wildcard" expression, e.g. ``"*"``,
- would raise an assertion error in the case where the query didn't
- contain any actual entities. This assertion is meant for other cases
- and was catching this one inadvertently.
-
- .. change::
- :tags: bug, examples
-
- Fixed bug in the versioned_history example where column-level INSERT
- defaults would prevent history values of NULL from being written.
-
- .. change::
- :tags: orm, bug, sqlite
- :tickets: 2969
-
- More fixes to SQLite "join rewriting"; the fix from :ticket:`2967`
- implemented right before the release of 0.9.3 affected the case where
- a UNION contained nested joins in it. "Join rewriting" is a feature
- with a wide range of possibilities and is the first intricate
- "SQL rewriting" feature we've introduced in years, so we're sort of
- going through a lot of iterations with it (not unlike eager loading
- back in the 0.2/0.3 series, polymorphic loading in 0.4/0.5). We should
- be there soon so thanks for bearing with us :).
-
-
-.. changelog::
- :version: 0.9.3
- :released: February 19, 2014
-
- .. change::
- :tags: orm, bug, sqlite
- :tickets: 2967
-
- Fixed bug in SQLite "join rewriting" where usage of an exists() construct
- would fail to be rewritten properly, such as when the exists is
- mapped to a column_property in an intricate nested-join scenario.
- Also fixed a somewhat related issue where join rewriting would fail
- on the columns clause of the SELECT statement if the targets were
- aliased tables, as opposed to individual aliased columns.
-
- .. change::
- :tags: sqlite, bug
-
- The SQLite dialect will now skip unsupported arguments when reflecting
- types; such as if it encounters a string like ``INTEGER(5)``, the
- :class:`_types.INTEGER` type will be instantiated without the "5" being included,
- based on detecting a ``TypeError`` on the first attempt.
-
- .. change::
- :tags: sqlite, bug
-
- Support has been added to SQLite type reflection to fully support
- the "type affinity" contract specified at https://www.sqlite.org/datatype3.html.
- In this scheme, keywords like ``INT``, ``CHAR``, ``BLOB`` or
- ``REAL`` located in the type name generically associate the type with
- one of five affinities. Pull request courtesy Erich Blume.
-
- .. seealso::
-
- :ref:`sqlite_type_reflection`
-
- .. change::
- :tags: postgresql, feature
-
- Added the :attr:`.TypeEngine.python_type` convenience accessor onto the
- :class:`_postgresql.ARRAY` type. Pull request courtesy Alexey Terentev.
-
- .. change::
- :tags: examples, feature
-
- Added optional "changed" column to the versioned rows example, as well
- as support for when the versioned :class:`_schema.Table` has an explicit
- :paramref:`_schema.Table.schema` argument. Pull request
- courtesy jplaverdure.
-
- .. change::
- :tags: bug, postgresql
- :tickets: 2946
-
- Added server version detection to the newly added dialect startup
- query for "show standard_conforming_strings"; as this variable was
- added as of PG 8.2, we skip the query for PG versions who report a
- version string earlier than that.
-
- .. change::
- :tags: bug, orm, declarative
- :tickets: 2950
-
- Fixed bug where :class:`.AbstractConcreteBase` would fail to be
- fully usable within declarative relationship configuration, as its
- string classname would not be available in the registry of classnames
- at mapper configuration time. The class now explicitly adds itself
- to the class registry, and additionally both :class:`.AbstractConcreteBase`
- as well as :class:`.ConcreteBase` set themselves up *before* mappers
- are configured within the :func:`.configure_mappers` setup, using
- the new :meth:`.MapperEvents.before_configured` event.
-
- .. change::
- :tags: feature, orm
-
- Added new :meth:`.MapperEvents.before_configured` event which allows
- an event at the start of :func:`.configure_mappers`, as well
- as ``__declare_first__()`` hook within declarative to complement
- ``__declare_last__()``.
-
- .. change::
- :tags: bug, mysql, cymysql
- :tickets: 2934
-
- Fixed bug in cymysql dialect where a version string such as
- ``'33a-MariaDB'`` would fail to parse properly. Pull request
- courtesy Matt Schmidt.
-
- .. change::
- :tags: bug, orm
- :tickets: 2949
-
- Fixed an 0.9 regression where ORM instance or mapper events applied
- to a base class such as a declarative base with the propagate=True
- flag would fail to apply to existing mapped classes which also
- used inheritance due to an assertion. Additionally, repaired an
- attribute error which could occur during removal of such an event,
- depending on how it was first assigned.
-
- .. change::
- :tags: bug, ext
-
- Fixed bug where the :class:`.AutomapBase` class of the
- new automap extension would fail if classes
- were pre-arranged in single or potentially joined inheritance patterns.
- The repaired joined inheritance issue could also potentially apply when
- using :class:`.DeferredReflection` as well.
-
-
- .. change::
- :tags: bug, sql
-
- Fixed regression in new "naming convention" feature where conventions
- would fail if the referred table in a foreign key contained a schema
- name. Pull request courtesy Thomas Farvour.
-
- .. change::
- :tags: bug, sql
-
- Fixed bug where so-called "literal render" of :func:`.bindparam`
- constructs would fail if the bind were constructed with a callable,
- rather than a direct value. This prevented ORM expressions
- from being rendered with the "literal_binds" compiler flag.
-
- .. change::
- :tags: bug, orm
- :tickets: 2935
-
- Improved the initialization logic of composite attributes such that
- calling ``MyClass.attribute`` will not require that the configure
- mappers step has occurred, e.g. it will just work without throwing
- any error.
-
- .. change::
- :tags: bug, orm
- :tickets: 2932
-
- More issues with [ticket:2932] first resolved in 0.9.2 where
- using a column key of the form ``<tablename>_<columnname>``
- matching that of an aliased column in the text would still not
- match at the ORM level, which is ultimately due to a core
- column-matching issue. Additional rules have been added so that the
- column ``_label`` is taken into account when working with a
- :class:`.TextAsFrom` construct or with literal columns.
-
-.. changelog::
- :version: 0.9.2
- :released: February 2, 2014
-
- .. change::
- :tags: bug, examples
-
- Added a tweak to the "history_meta" example where the check for
- "history" on a relationship-bound attribute will now no longer emit
- any SQL if the relationship is unloaded.
-
- .. change::
- :tags: feature, sql
-
- Added :paramref:`.MetaData.reflect.dialect_kwargs`
- to support dialect-level reflection options for all :class:`_schema.Table`
- objects reflected.
-
- .. change::
- :tags: feature, postgresql
- :tickets: 2922
-
- Added a new dialect-level argument ``postgresql_ignore_search_path``;
- this argument is accepted by both the :class:`_schema.Table` constructor
- as well as by the :meth:`_schema.MetaData.reflect` method. When in use
- against PostgreSQL, a foreign-key referenced table which specifies
- a remote schema name will retain that schema name even if the name
- is present in the ``search_path``; the default behavior since 0.7.3
- has been that schemas present in ``search_path`` would not be copied
- to reflected :class:`_schema.ForeignKey` objects. The documentation has been
- updated to describe in detail the behavior of the ``pg_get_constraintdef()``
- function and how the ``postgresql_ignore_search_path`` feature essentially
- determines if we will honor the schema qualification reported by
- this function or not.
-
- .. seealso::
-
- :ref:`postgresql_schema_reflection`
-
- .. change::
- :tags: bug, sql
- :tickets: 2913
-
- The behavior of :meth:`_schema.Table.tometadata` has been adjusted such that
- the schema target of a :class:`_schema.ForeignKey` will not be changed unless
- that schema matches that of the parent table. That is, if
- a table "schema_a.user" has a foreign key to "schema_b.order.id",
- the "schema_b" target will be maintained whether or not the
- "schema" argument is passed to :meth:`_schema.Table.tometadata`. However
- if a table "schema_a.user" refers to "schema_a.order.id", the presence
- of "schema_a" will be updated on both the parent and referred tables.
- This is a behavioral change hence isn't likely to be backported to
- 0.8; it is assumed that the previous behavior is pretty buggy
- however and that it's unlikely anyone was relying upon it.
-
- Additionally, a new parameter has been added
- :paramref:`.Table.tometadata.referred_schema_fn`. This refers to a
- callable function which will be used to determine the new referred
- schema for any :class:`_schema.ForeignKeyConstraint` encountered in the
- tometadata operation. This callable can be used to revert to the
- previous behavior or to customize how referred schemas are treated
- on a per-constraint basis.
-
- .. change::
- :tags: bug, orm
- :tickets: 2932
-
- Fixed bug in new :class:`.TextAsFrom` construct where :class:`_schema.Column`-
- oriented row lookups were not matching up to the ad-hoc :class:`.ColumnClause`
- objects that :class:`.TextAsFrom` generates, thereby making it not
- usable as a target in :meth:`_query.Query.from_statement`. Also fixed
- :meth:`_query.Query.from_statement` mechanics to not mistake a :class:`.TextAsFrom`
- for a :class:`_expression.Select` construct. This bug is also an 0.9 regression
- as the :meth:`_expression.TextClause.columns` method is called to accommodate the
- :paramref:`_expression.text.typemap` argument.
-
- .. change::
- :tags: feature, sql
- :tickets: 2923
-
- Added a new feature which allows automated naming conventions to be
- applied to :class:`.Constraint` and :class:`.Index` objects. Based
- on a recipe in the wiki, the new feature uses schema-events to set up
- names as various schema objects are associated with each other. The
- events then expose a configuration system through a new argument
- :paramref:`_schema.MetaData.naming_convention`. This system allows production
- of both simple and custom naming schemes for constraints and indexes
- on a per-:class:`_schema.MetaData` basis.
-
- .. seealso::
-
- :ref:`constraint_naming_conventions`
-
- .. change::
- :tags: bug, orm
- :tickets: 2921
-
- Added a new directive used within the scope of an attribute "set" operation
- to disable autoflush, in the case that the attribute needs to lazy-load
- the "old" value, as in when replacing one-to-one values or some
- kinds of many-to-one. A flush at this point otherwise occurs
- at the point that the attribute is None and can cause NULL violations.
-
- .. change::
- :tags: feature, orm
-
- Added a new parameter :paramref:`.Operators.op.is_comparison`. This
- flag allows a custom op from :meth:`.Operators.op` to be considered
- as a "comparison" operator, thus usable for custom
- :paramref:`_orm.relationship.primaryjoin` conditions.
-
- .. seealso::
-
- :ref:`relationship_custom_operator`
-
- .. change::
- :tags: bug, sqlite
-
- Fixed bug whereby SQLite compiler failed to propagate compiler arguments
- such as "literal binds" into a CAST expression.
-
- .. change::
- :tags: bug, sql
-
- Fixed bug whereby binary type would fail in some cases
- if used with a "test" dialect, such as a DefaultDialect or other
- dialect with no DBAPI.
-
- .. change::
- :tags: bug, sql, py3k
-
- Fixed bug where "literal binds" wouldn't work with a bound parameter
- that's a binary type. A similar, but different, issue is fixed
- in 0.8.
-
- .. change::
- :tags: bug, sql
- :tickets: 2927
-
- Fixed regression whereby the "annotation" system used by the ORM was leaking
- into the names used by standard functions in :mod:`sqlalchemy.sql.functions`,
- such as ``func.coalesce()`` and ``func.max()``. Using these functions
- in ORM attributes and thus producing annotated versions of them could
- corrupt the actual function name rendered in the SQL.
-
- .. change::
- :tags: bug, sql
- :tickets: 2924, 2848
-
- Fixed 0.9 regression where the new sortable support for :class:`.RowProxy`
- would lead to ``TypeError`` when compared to non-tuple types as it attempted
- to apply tuple() to the "other" object unconditionally. The
- full range of Python comparison operators have now been implemented on
- :class:`.RowProxy`, using an approach that guarantees a comparison
- system that is equivalent to that of a tuple, and the "other" object
- is only coerced if it's an instance of RowProxy.
-
- .. change::
- :tags: bug, orm
- :tickets: 2918
-
- Fixed an 0.9 regression where the automatic aliasing applied by
- :class:`_query.Query` and in other situations where selects or joins
- were aliased (such as joined table inheritance) could fail if a
- user-defined :class:`_schema.Column` subclass were used in the expression.
- In this case, the subclass would fail to propagate ORM-specific
- "annotations" along needed by the adaptation. The "expression
- annotations" system has been corrected to account for this case.
-
- .. change::
- :tags: feature, orm
-
- Support is improved for supplying a :func:`_expression.join` construct as the
- target of :paramref:`_orm.relationship.secondary` for the purposes
- of creating very complex :func:`_orm.relationship` join conditions.
- The change includes adjustments to query joining, joined eager loading
- to not render a SELECT subquery, changes to lazy loading such that
- the "secondary" target is properly included in the SELECT, and
- changes to declarative to better support specification of a
- join() object with classes as targets.
-
- The new use case is somewhat experimental, but a new documentation section
- has been added.
-
- .. seealso::
-
- :ref:`composite_secondary_join`
-
- .. change::
- :tags: bug, mysql, sql
- :tickets: 2917
-
- Added new test coverage for so-called "down adaptions" of SQL types,
- where a more specific type is adapted to a more generic one - this
- use case is needed by some third party tools such as ``sqlacodegen``.
- The specific cases that needed repair within this test suite were that
- of :class:`.mysql.ENUM` being downcast into a :class:`_types.Enum`,
- and that of SQLite date types being cast into generic date types.
- The ``adapt()`` method needed to become more specific here to counteract
- the removal of a "catch all" ``**kwargs`` collection on the base
- :class:`.TypeEngine` class that was removed in 0.9.
-
- .. change::
- :tags: feature, sql
- :tickets: 2910
-
- Options can now be specified on a :class:`.PrimaryKeyConstraint` object
- independently of the specification of columns in the table with
- the ``primary_key=True`` flag; use a :class:`.PrimaryKeyConstraint`
- object with no columns in it to achieve this result.
-
- Previously, an explicit :class:`.PrimaryKeyConstraint` would have the
- effect of those columns marked as ``primary_key=True`` being ignored;
- since this is no longer the case, the :class:`.PrimaryKeyConstraint`
- will now assert that either one style or the other is used to specify
- the columns, or if both are present, that the column lists match
- exactly. If an inconsistent set of columns in the
- :class:`.PrimaryKeyConstraint`
- and within the :class:`_schema.Table` marked as ``primary_key=True`` are
- present, a warning is emitted, and the list of columns is taken
- only from the :class:`.PrimaryKeyConstraint` alone as was the case
- in previous releases.
-
-
-
- .. seealso::
-
- :class:`.PrimaryKeyConstraint`
-
- .. change::
- :tags: feature, sql
- :tickets: 2866
-
- The system by which schema constructs and certain SQL constructs
- accept dialect-specific keyword arguments has been enhanced. This
- system includes commonly the :class:`_schema.Table` and :class:`.Index` constructs,
- which accept a wide variety of dialect-specific arguments such as
- ``mysql_engine`` and ``postgresql_where``, as well as the constructs
- :class:`.PrimaryKeyConstraint`, :class:`.UniqueConstraint`,
- :class:`_expression.Update`, :class:`_expression.Insert` and :class:`_expression.Delete`, and also
- newly added kwarg capability to :class:`_schema.ForeignKeyConstraint`
- and :class:`_schema.ForeignKey`. The change is that participating dialects
- can now specify acceptable argument lists for these constructs, allowing
- an argument error to be raised if an invalid keyword is specified for
- a particular dialect. If the dialect portion of the keyword is unrecognized,
- a warning is emitted only; while the system will actually make use
- of setuptools entrypoints in order to locate non-local dialects,
- the use case where certain dialect-specific arguments are used
- in an environment where that third-party dialect is uninstalled remains
- supported. Dialects also have to explicitly opt-in to this system,
- so that external dialects which aren't making use of this system
- will remain unaffected.
-
- .. change::
- :tags: bug, sql
-
- A :class:`.UniqueConstraint` created inline with a :class:`_schema.Table`
- that has no columns within it will be skipped. Pullreq courtesy
- Derek Harland.
-
- .. change::
- :tags: feature, mssql
-
- Added an option ``mssql_clustered`` to the :class:`.UniqueConstraint`
- and :class:`.PrimaryKeyConstraint` constructs; on SQL Server, this adds
- the ``CLUSTERED`` keyword to the constraint construct within DDL.
- Pullreq courtesy Derek Harland.
-
- .. change::
- :tags: bug, sql, orm
- :tickets: 2912
-
- Fixed the multiple-table "UPDATE..FROM" construct, only usable on
- MySQL, to correctly render the SET clause among multiple columns
- with the same name across tables. This also changes the name used for
- the bound parameter in the SET clause to "<tablename>_<colname>" for
- the non-primary table only; as this parameter is typically specified
- using the :class:`_schema.Column` object directly this should not have an
- impact on applications. The fix takes effect for both
- :meth:`_schema.Table.update` as well as :meth:`_query.Query.update` in the ORM.
-
- .. change::
- :tags: bug, oracle
- :tickets: 2911
-
- It's been observed that the usage of a cx_Oracle "outputtypehandler"
- in Python 2.xx in order to coerce string values to Unicode is inordinately
- expensive; even though cx_Oracle is written in C, when you pass the
- Python ``unicode`` primitive to cursor.var() and associate with an output
- handler, the library counts every conversion as a Python function call
- with all the requisite overhead being recorded; this *despite* the fact
- when running in Python 3, all strings are also unconditionally coerced
- to unicode but it does *not* incur this overhead,
- meaning that cx_Oracle is failing to use performant techniques in Py2K.
- As SQLAlchemy cannot easily select for this style of type handler on a
- per-column basis, the handler was assembled unconditionally thereby
- adding the overhead to all string access.
-
- So this logic has been replaced with SQLAlchemy's own unicode
- conversion system, which now
- only takes effect in Py2K for columns that are requested as unicode.
- When C extensions are used, SQLAlchemy's system appears to be 2-3x faster than
- cx_Oracle's. Additionally, SQLAlchemy's unicode conversion has been
- enhanced such that when the "conditional" converter is required
- (now needed for the Oracle backend), the check for "already unicode" is now
- performed in C and no longer introduces significant overhead.
-
- This change has two impacts on the cx_Oracle backend. One is that
- string values in Py2K which aren't specifically requested with the
- Unicode type or convert_unicode=True will now come back as ``str``,
- not ``unicode`` - this behavior is similar to a backend such as
- MySQL. Additionally, when unicode values are requested with the cx_Oracle
- backend, if the C extensions are *not* used, there is now an additional
- overhead of an isinstance() check per column. This tradeoff has been
- made as it can be worked around and no longer places a performance burden
- on the likely majority of Oracle result columns that are non-unicode
- strings.
-
- .. change::
- :tags: bug, orm
- :tickets: 2908
-
- Fixed a bug involving the new flattened JOIN structures which
- are used with :func:`_orm.joinedload()` (thereby causing a regression
- in joined eager loading) as well as :func:`.aliased`
- in conjunction with the ``flat=True`` flag and joined-table inheritance;
- basically multiple joins across a "parent JOIN sub" entity using different
- paths to get to a target class wouldn't form the correct ON conditions.
- An adjustment / simplification made in the mechanics of figuring
- out the "left side" of the join in the case of an aliased, joined-inh
- class repairs the issue.
-
- .. change::
- :tags: bug, mysql
-
- The MySQL CAST compilation now takes into account aspects of a string
- type such as "charset" and "collation". While MySQL wants all character-
- based CAST calls to use the CHAR type, we now create a real CHAR
- object at CAST time and copy over all the parameters it has, so that
- an expression like ``cast(x, mysql.TEXT(charset='utf8'))`` will
- render ``CAST(t.col AS CHAR CHARACTER SET utf8)``.
-
- .. change::
- :tags: bug, mysql
- :tickets: 2906
-
- Added new "unicode returns" detection to the MySQL dialect and
- to the default dialect system overall, such that any dialect
- can add extra "tests" to the on-first-connect "does this DBAPI
- return unicode directly?" detection. In this case, we are
- adding a check specifically against the "utf8" encoding with
- an explicit "utf8_bin" collation type (after checking that
- this collation is available) to test for some buggy unicode
- behavior observed with MySQLdb version 1.2.3. While MySQLdb
- has resolved this issue as of 1.2.4, the check here should
- guard against regressions. The change also allows the "unicode"
- checks to log in the engine logs, which was not previously
- the case.
-
- .. change::
- :tags: bug, mysql, pool, engine
- :tickets: 2907
-
- :class:`_engine.Connection` now associates a new
- :class:`.RootTransaction` or :class:`.TwoPhaseTransaction`
- with its immediate :class:`._ConnectionFairy` as a "reset handler"
- for the span of that transaction, which takes over the task
- of calling commit() or rollback() for the "reset on return" behavior
- of :class:`_pool.Pool` if the transaction was not otherwise completed.
- This resolves the issue that a picky transaction
- like that of MySQL two-phase will be
- properly closed out when the connection is closed without an
- explicit rollback or commit (e.g. no longer raises "XAER_RMFAIL"
- in this case - note this only shows up in logging as the exception
- is not propagated within pool reset).
- This issue would arise e.g. when using an orm
- :class:`.Session` with ``twophase`` set, and then
- :meth:`.Session.close` is called without an explicit rollback or
- commit. The change also has the effect that you will now see
- an explicit "ROLLBACK" in the logs when using a :class:`.Session`
- object in non-autocommit mode regardless of how that session was
- discarded. Thanks to Jeff Dairiki and Laurence Rowe for isolating
- the issue here.
-
- .. change::
- :tags: feature, pool, engine
-
- Added a new pool event :meth:`_events.PoolEvents.invalidate`. Called when
- a DBAPI connection is to be marked as "invalidated" and discarded
- from the pool.
-
- .. change::
- :tags: bug, pool
-
- The argument names for the :meth:`_events.PoolEvents.reset` event have been
- renamed to ``dbapi_connection`` and ``connection_record`` in order
- to maintain consistency with all the other pool events. It is expected
- that any existing listeners for this relatively new and
- seldom-used event are using positional style to receive arguments in
- any case.
-
- .. change::
- :tags: bug, py3k, cextensions
-
- Fixed an issue where the C extensions in Py3K are using the wrong API
- to specify the top-level module function, which breaks
- in Python 3.4b2. Py3.4b2 changes PyMODINIT_FUNC to return
- "void" instead of ``PyObject *``, so we now make sure to use
- "PyMODINIT_FUNC" instead of ``PyObject *`` directly. Pull request
- courtesy cgohlke.
-
- .. change::
- :tags: bug, schema
-
- Restored :class:`sqlalchemy.schema.SchemaVisitor` to the ``.schema``
- module. Pullreq courtesy Sean Dague.
-
-.. changelog::
- :version: 0.9.1
- :released: January 5, 2014
-
- .. change::
- :tags: bug, orm, events
- :tickets: 2905
-
- Fixed regression where using a ``functools.partial()`` with the event
- system would cause a recursion overflow due to usage of inspect.getargspec()
- on it in order to detect a legacy calling signature for certain events,
- and apparently there's no way to do this with a partial object. Instead
- we skip the legacy check and assume the modern style; the check itself
- now only occurs for the SessionEvents.after_bulk_update and
- SessionEvents.after_bulk_delete events. Those two events will require
- the new signature style if assigned to a "partial" event listener.
-
- .. change::
- :tags: feature, orm, extensions
-
- A new, **experimental** extension :mod:`sqlalchemy.ext.automap` is added.
- This extension expands upon the functionality of Declarative as well as
- the :class:`.DeferredReflection` class to produce a base class which
- automatically generates mapped classes *and relationships* based on
- table metadata.
-
- .. seealso::
-
- :ref:`feature_automap`
-
- :ref:`automap_toplevel`
-
- .. change::
- :tags: feature, sql
-
- Conjunctions like :func:`.and_` and :func:`.or_` can now accept
- Python generators as a single argument, e.g.::
-
- and_(x == y for x, y in tuples)
-
- The logic here looks for a single argument ``*args`` where the first
- element is an instance of ``types.GeneratorType``.
-
- .. change::
- :tags: feature, schema
-
- The :paramref:`_schema.Table.extend_existing` and :paramref:`_schema.Table.autoload_replace`
- parameters are now available on the :meth:`_schema.MetaData.reflect`
- method.
-
- .. change::
- :tags: bug, orm, declarative
-
- Fixed an extremely unlikely memory issue where when using
- :class:`.DeferredReflection`
- to define classes pending for reflection, if some subset of those
- classes were discarded before the :meth:`.DeferredReflection.prepare`
- method were called to reflect and map the class, a strong reference
- to the class would remain held within the declarative internals.
- This internal collection of "classes to map" now uses weak
- references against the classes themselves.
-
- .. change::
- :tags: bug, orm
-
- Fixed bug where using new :attr:`.Session.info` attribute would fail
- if the ``.info`` argument were only passed to the :class:`.sessionmaker`
- creation call but not to the object itself. Courtesy Robin Schoonover.
-
- .. change::
- :tags: bug, orm
- :tickets: 2901
-
- Fixed regression where we don't check the given name against the
- correct string class when setting up a backref based on a name,
- therefore causing the error "too many values to unpack". This was
- related to the Py3k conversion.
-
- .. change::
- :tags: bug, orm, declarative
- :tickets: 2900
-
- A quasi-regression where apparently in 0.8 you can set a class-level
- attribute on declarative to simply refer directly to an :class:`.InstrumentedAttribute`
- on a superclass or on the class itself, and it
- acts more or less like a synonym; in 0.9, this fails to set up enough
- bookkeeping to keep up with the more liberalized backref logic
- from :ticket:`2789`. Even though this use case was never directly
- considered, it is now detected by declarative at the "setattr()" level
- as well as when setting up a subclass, and the mirrored/renamed attribute
- is now set up as a :func:`.synonym` instead.
-
- .. change::
- :tags: bug, orm
- :tickets: 2903
-
- Fixed regression where we apparently still create an implicit
- alias when saying query(B).join(B.cs), where "C" is a joined inh
- class; however, this implicit alias was created only considering
- the immediate left side, and not a longer chain of joins along different
- joined-inh subclasses of the same base. As long as we're still
- implicitly aliasing in this case, the behavior is dialed back a bit
- so that it will alias the right side in a wider variety of cases.
-
-.. changelog::
- :version: 0.9.0
- :released: December 30, 2013
-
- .. change::
- :tags: bug, orm, declarative
- :tickets: 2828
-
- Declarative does an extra check to detect if the same
- :class:`_schema.Column` is mapped multiple times under different properties
- (which typically should be a :func:`.synonym` instead) or if two
- or more :class:`_schema.Column` objects are given the same name, raising
- a warning if this condition is detected.
-
- .. change::
- :tags: bug, firebird
- :tickets: 2898
-
- Changed the queries used by Firebird to list table and view names
- to query from the ``rdb$relations`` view instead of the
- ``rdb$relation_fields`` and ``rdb$view_relations`` views.
- Variants of both the old and new queries are mentioned on many
- FAQ and blogs, however the new queries are taken straight from
- the "Firebird FAQ" which appears to be the most official source
- of info.
-
- .. change::
- :tags: bug, mysql
- :tickets: 2893
-
- Improvements to the system by which SQL types generate within
- ``__repr__()``, particularly with regards to the MySQL integer/numeric/
- character types which feature a wide variety of keyword arguments.
- The ``__repr__()`` is important for use with Alembic autogenerate
- for when Python code is rendered in a migration script.
-
- .. change::
- :tags: feature, postgresql
- :tickets: 2581
-
- Support for PostgreSQL JSON has been added, using the new
- :class:`_types.JSON` type. Huge thanks to Nathan Rice for
- implementing and testing this.
-
- .. change::
- :tags: bug, sql
-
- The :func:`.cast` function, when given a plain literal value,
- will now apply the given type to the given literal value on the
- bind parameter side according to the type given to the cast,
- in the same manner as that of the :func:`.type_coerce` function.
- However unlike :func:`.type_coerce`, this only takes effect if a
- non-clauseelement value is passed to :func:`.cast`; an existing typed
- construct will retain its type.
-
- .. change::
- :tags: bug, postgresql
-
- Now using psycopg2 UNICODEARRAY extension for handling unicode arrays
- with psycopg2 + normal "native unicode" mode, in the same way the
- UNICODE extension is used.
-
- .. change::
- :tags: bug, sql
- :tickets: 2883
-
- The :class:`_schema.ForeignKey` class more aggressively checks the given
- column argument. If not a string, it checks that the object is
- at least a :class:`.ColumnClause`, or an object that resolves to one,
- and that the ``.table`` attribute, if present, refers to a
- :class:`_expression.TableClause` or subclass, and not something like an
- :class:`_expression.Alias`. Otherwise, a :class:`.ArgumentError` is raised.
-
-
- .. change::
- :tags: feature, orm
-
- The :class:`.exc.StatementError` or DBAPI-related subclass
- now can accommodate additional information about the "reason" for
- the exception; the :class:`.Session` now adds some detail to it
- when the exception occurs within an autoflush. This approach
- is taken as opposed to combining :class:`.FlushError` with
- a Python 3 style "chained exception" approach so as to maintain
- compatibility both with Py2K code as well as code that already
- catches ``IntegrityError`` or similar.
-
- .. change::
- :tags: feature, postgresql
-
- Added support for PostgreSQL TSVECTOR via the
- :class:`_postgresql.TSVECTOR` type. Pull request courtesy
- Noufal Ibrahim.
-
- .. change::
- :tags: feature, engine
- :tickets: 2875
-
- The :func:`.engine_from_config` function has been improved so that
- we will be able to parse dialect-specific arguments from string
- configuration dictionaries. Dialect classes can now provide their
- own list of parameter types and string-conversion routines.
- The feature is not yet used by the built-in dialects, however.
-
- .. change::
- :tags: bug, sql
- :tickets: 2879
-
- The precedence rules for the :meth:`.ColumnOperators.collate` operator
- have been modified, such that the COLLATE operator is now of lower
- precedence than the comparison operators. This has the effect that
- a COLLATE applied to a comparison will not render parenthesis
- around the comparison, which is not parsed by backends such as
- MSSQL. The change is backwards incompatible for those setups that
- were working around the issue by applying :meth:`.Operators.collate`
- to an individual element of the comparison expression,
- rather than the comparison expression as a whole.
-
- .. seealso::
-
- :ref:`migration_2879`
-
- .. change::
- :tags: bug, orm, declarative
- :tickets: 2865
-
- The :class:`.DeferredReflection` class has been enhanced to provide
- automatic reflection support for the "secondary" table referred
- to by a :func:`_orm.relationship`. "secondary", when specified
- either as a string table name, or as a :class:`_schema.Table` object with
- only a name and :class:`_schema.MetaData` object will also be included
- in the reflection process when :meth:`.DeferredReflection.prepare`
- is called.
-
- .. change::
- :tags: feature, orm, backrefs
- :tickets: 1535
-
- Added new argument ``include_backrefs=True`` to the
- :func:`.validates` function; when set to False, a validation event
- will not be triggered if the event was initiated as a backref to
- an attribute operation from the other side.
-
- .. seealso::
-
- :ref:`feature_1535`
-
- .. change::
- :tags: bug, orm, collections, py3k
-
- Added support for the Python 3 method ``list.clear()`` within
- the ORM collection instrumentation system; pull request
- courtesy Eduardo Schettino.
-
- .. change::
- :tags: bug, postgresql
- :tickets: 2878
-
- Fixed bug where values within an ENUM weren't escaped for single
- quote signs. Note that this is backwards-incompatible for existing
- workarounds that manually escape the single quotes.
-
- .. seealso::
-
- :ref:`migration_2878`
-
- .. change::
- :tags: bug, orm, declarative
-
- Fixed bug where in Py2K a unicode literal would not be accepted
- as the string name of a class or other argument within
- declarative using :func:`_orm.relationship`.
-
- .. change::
- :tags: feature, sql
- :tickets: 2877, 2882
-
- New improvements to the :func:`_expression.text` construct, including
- more flexible ways to set up bound parameters and return types;
- in particular, a :func:`_expression.text` can now be turned into a full
- FROM-object, embeddable in other statements as an alias or CTE
- using the new method :meth:`_expression.TextClause.columns`. The :func:`_expression.text`
- construct can also render "inline" bound parameters when the construct
- is compiled in a "literal bound" context.
-
- .. seealso::
-
- :ref:`feature_2877`
-
- .. change::
- :tags: feature, sql
-
- A new API for specifying the ``FOR UPDATE`` clause of a ``SELECT``
- is added with the new :meth:`_expression.GenerativeSelect.with_for_update` method.
- This method supports a more straightforward system of setting
- dialect-specific options compared to the ``for_update`` keyword
- argument of :func:`_expression.select`, and also includes support for the
- SQL standard ``FOR UPDATE OF`` clause. The ORM also includes
- a new corresponding method :meth:`_query.Query.with_for_update`.
- Pull request courtesy Mario Lassnig.
-
- .. seealso::
-
- :ref:`feature_github_42`
-
- .. change::
- :tags: feature, orm
-
- A new API for specifying the ``FOR UPDATE`` clause of a ``SELECT``
- is added with the new :meth:`_query.Query.with_for_update` method,
- to complement the new :meth:`_expression.GenerativeSelect.with_for_update` method.
- Pull request courtesy Mario Lassnig.
-
- .. seealso::
-
- :ref:`feature_github_42`
-
- .. change::
- :tags: bug, engine
- :tickets: 2873
-
- The :func:`_sa.create_engine` routine and the related :func:`.make_url`
- function no longer considers the ``+`` sign to be a space within the
- password field. The parsing in this area has been adjusted to match
- more closely to how RFC 1738 handles these tokens, in that both
- ``username`` and ``password`` expect only ``:``, ``@``, and ``/`` to be
- encoded.
-
- .. seealso::
-
- :ref:`migration_2873`
-
-
- .. change::
- :tags: bug, orm
- :tickets: 2872
-
- Some refinements to the :class:`.AliasedClass` construct with regards
- to descriptors, like hybrids, synonyms, composites, user-defined
- descriptors, etc. The attribute
- adaptation which goes on has been made more robust, such that if a descriptor
- returns another instrumented attribute, rather than a compound SQL
- expression element, the operation will still proceed.
- Additionally, the "adapted" operator will retain its class; previously,
- a change in class from ``InstrumentedAttribute`` to ``QueryableAttribute``
- (a superclass) would interact with Python's operator system such that
- an expression like ``aliased(MyClass.x) > MyClass.x`` would reverse itself
- to read ``myclass.x < myclass_1.x``. The adapted attribute will also
- refer to the new :class:`.AliasedClass` as its parent which was not
- always the case before.
-
- .. change::
- :tags: feature, sql
- :tickets: 2867
-
- The precision used when coercing a returned floating point value to
- Python ``Decimal`` via string is now configurable. The
- flag ``decimal_return_scale`` is now supported by all :class:`.Numeric`
- and :class:`.Float` types, which will ensure this many digits are taken
- from the native floating point value when it is converted to string.
- If not present, the type will make use of the value of ``.scale``, if
- the type supports this setting and it is non-None. Otherwise the original
- default length of 10 is used.
-
- .. seealso::
-
- :ref:`feature_2867`
-
- .. change::
- :tags: bug, schema
- :tickets: 2868
-
- Fixed a regression caused by :ticket:`2812` where the repr() for
- table and column names would fail if the name contained non-ascii
- characters.
-
- .. change::
- :tags: bug, engine
- :tickets: 2848
-
- The :class:`.RowProxy` object is now sortable in Python as a regular
- tuple is; this is accomplished via ensuring tuple() conversion on
- both sides within the ``__eq__()`` method as well as
- the addition of a ``__lt__()`` method.
-
- .. seealso::
-
- :ref:`migration_2848`
-
- .. change::
- :tags: bug, orm
- :tickets: 2833
-
- The ``viewonly`` flag on :func:`_orm.relationship` will now prevent
- attribute history from being written on behalf of the target attribute.
- This has the effect of the object not being written to the
- Session.dirty list if it is mutated. Previously, the object would
- be present in Session.dirty, but no change would take place on behalf
- of the modified attribute during flush. The attribute still emits
- events such as backref events and user-defined events and will still
- receive mutations from backrefs.
-
- .. seealso::
-
- :ref:`migration_2833`
-
- .. change::
- :tags: bug, orm
-
- Added support for new :attr:`.Session.info` attribute to
- :class:`.scoped_session`.
-
- .. change::
- :tags: removed
-
- The "informix" and "informixdb" dialects have been removed; the code
- is now available as a separate repository on Bitbucket. The IBM-DB
- project has provided production-level Informix support since the
- informixdb dialect was first added.
-
- .. change::
- :tags: bug, orm
-
- Fixed bug where usage of new :class:`.Bundle` object would cause
- the :attr:`_query.Query.column_descriptions` attribute to fail.
-
- .. change::
- :tags: bug, examples
-
- Fixed bug which prevented history_meta recipe from working with
- joined inheritance schemes more than one level deep.
-
- .. change::
- :tags: bug, orm, sql, sqlite
- :tickets: 2858
-
- Fixed a regression introduced by the join rewriting feature of
- :ticket:`2369` and :ticket:`2587` where a nested join with one side
- already an aliased select would fail to translate the ON clause on the
- outside correctly; in the ORM this could be seen when using a
- SELECT statement as a "secondary" table.
-
-.. changelog::
- :version: 0.9.0b1
- :released: October 26, 2013
-
- .. change::
- :tags: feature, orm
- :tickets: 2810
-
- The association proxy now returns ``None`` when fetching a scalar
- attribute off of a scalar relationship, where the scalar relationship
- itself points to ``None``, instead of raising an ``AttributeError``.
-
- .. seealso::
-
- :ref:`migration_2810`
-
- .. change::
- :tags: feature, sql, postgresql, mysql
- :tickets: 2183
-
- The PostgreSQL and MySQL dialects now support reflection/inspection
- of foreign key options, including ON UPDATE, ON DELETE. PostgreSQL
- also reflects MATCH, DEFERRABLE, and INITIALLY. Courtesy ijl.
-
- .. change::
- :tags: bug, mysql
- :tickets: 2839
-
- Fix and test parsing of MySQL foreign key options within reflection;
- this complements the work in :ticket:`2183` where we begin to support
- reflection of foreign key options such as ON UPDATE/ON DELETE
- cascade.
-
- .. change::
- :tags: bug, orm
- :tickets: 2787
-
- :func:`.attributes.get_history()` when used with a scalar column-mapped
- attribute will now honor the "passive" flag
- passed to it; as this defaults to ``PASSIVE_OFF``, the function will
- by default query the database if the value is not present.
- This is a behavioral change vs. 0.8.
-
- .. seealso::
-
- :ref:`change_2787`
-
- .. change::
- :tags: feature, orm
- :tickets: 2787
-
- Added new method :meth:`.AttributeState.load_history`, works like
- :attr:`.AttributeState.history` but also fires loader callables.
-
- .. seealso::
-
- :ref:`change_2787`
-
-
- .. change::
- :tags: feature, sql
- :tickets: 2850
-
- A :func:`.bindparam` construct with a "null" type (e.g. no type
- specified) is now copied when used in a typed expression, and the
- new copy is assigned the actual type of the compared column. Previously,
- this logic would occur on the given :func:`.bindparam` in place.
- Additionally, a similar process now occurs for :func:`.bindparam` constructs
- passed to :meth:`.ValuesBase.values` for an :class:`_expression.Insert` or
- :class:`_expression.Update` construct, within the compilation phase of the
- construct.
-
- These are both subtle behavioral changes which may impact some
- usages.
-
- .. seealso::
-
- :ref:`migration_2850`
-
- .. change::
- :tags: feature, sql
- :tickets: 2804, 2823, 2734
-
- An overhaul of expression handling for special symbols particularly
- with conjunctions, e.g.
- ``None`` :func:`_expression.null` :func:`_expression.true`
- :func:`_expression.false`, including consistency in rendering NULL
- in conjunctions, "short-circuiting" of :func:`.and_` and :func:`.or_`
- expressions which contain boolean constants, and rendering of
- boolean constants and expressions as compared to "1" or "0" for backends
- that don't feature ``true``/``false`` constants.
-
- .. seealso::
-
- :ref:`migration_2804`
-
- .. change::
- :tags: feature, sql
- :tickets: 2838
-
- The typing system now handles the task of rendering "literal bind" values,
- e.g. values that are normally bound parameters but due to context must
- be rendered as strings, typically within DDL constructs such as
- CHECK constraints and indexes (note that "literal bind" values
- become used by DDL as of :ticket:`2742`). A new method
- :meth:`.TypeEngine.literal_processor` serves as the base, and
- :meth:`.TypeDecorator.process_literal_param` is added to allow wrapping
- of a native literal rendering method.
-
- .. seealso::
-
- :ref:`change_2838`
-
- .. change::
- :tags: feature, sql
- :tickets: 2716
-
- The :meth:`_schema.Table.tometadata` method now produces copies of
- all :attr:`.SchemaItem.info` dictionaries from all :class:`.SchemaItem`
- objects within the structure including columns, constraints,
- foreign keys, etc. As these dictionaries
- are copies, they are independent of the original dictionary.
- Previously, only the ``.info`` dictionary of :class:`_schema.Column` was transferred
- within this operation, and it was only linked in place, not copied.
-
- .. change::
- :tags: feature, postgresql
- :tickets: 2840
-
- Added support for rendering ``SMALLSERIAL`` when a :class:`.SmallInteger`
- type is used on a primary key autoincrement column, based on server
- version detection of PostgreSQL version 9.2 or greater.
-
- .. change::
- :tags: feature, mysql
- :tickets: 2817
-
- The MySQL :class:`.mysql.SET` type now features the same auto-quoting
- behavior as that of :class:`.mysql.ENUM`. Quotes are not required when
- setting up the value, but quotes that are present will be auto-detected
- along with a warning. This also helps with Alembic where
- the SET type doesn't render with quotes.
-
- .. change::
- :tags: feature, sql
-
- The ``default`` argument of :class:`_schema.Column` now accepts a class
- or object method as an argument, in addition to a standalone function;
- will properly detect if the "context" argument is accepted or not.
-
- .. change::
- :tags: bug, sql
- :tickets: 2835
-
- The "name" attribute is set on :class:`.Index` before the "attach"
- events are called, so that attachment events can be used to dynamically
- generate a name for the index based on the parent table and/or
- columns.
-
- .. change::
- :tags: bug, engine
- :tickets: 2748
-
- The method signature of :meth:`.Dialect.reflecttable`, which in
- all known cases is provided by :class:`.DefaultDialect`, has been
- tightened to expect ``include_columns`` and ``exclude_columns``
- arguments without any kw option, reducing ambiguity - previously
- ``exclude_columns`` was missing.
-
- .. change::
- :tags: bug, sql
- :tickets: 2831
-
- The erroneous kw arg "schema" has been removed from the :class:`_schema.ForeignKey`
- object. this was an accidental commit that did nothing; a warning is raised
- in 0.8.3 when this kw arg is used.
-
- .. change::
- :tags: feature, orm
- :tickets: 1418
-
- Added a new load option :func:`_orm.load_only`. This allows a series
- of column names to be specified as loading "only" those attributes,
- deferring the rest.
-
- .. change::
- :tags: feature, orm
- :tickets: 1418
-
- The system of loader options has been entirely rearchitected to build
- upon a much more comprehensive base, the :class:`_orm.Load` object. This
- base allows any common loader option like :func:`_orm.joinedload`,
- :func:`.defer`, etc. to be used in a "chained" style for the purpose
- of specifying options down a path, such as ``joinedload("foo").subqueryload("bar")``.
- The new system supersedes the usage of dot-separated path names,
- multiple attributes within options, and the usage of ``_all()`` options.
-
- .. seealso::
-
- :ref:`feature_1418`
-
- .. change::
- :tags: feature, orm
- :tickets: 2824
-
- The :func:`.composite` construct now maintains the return object
- when used in a column-oriented :class:`_query.Query`, rather than expanding
- out into individual columns. This makes use of the new :class:`.Bundle`
- feature internally. This behavior is backwards incompatible; to
- select from a composite column which will expand out, use
- ``MyClass.some_composite.clauses``.
-
- .. seealso::
-
- :ref:`migration_2824`
-
- .. change::
- :tags: feature, orm
- :tickets: 2824
-
- A new construct :class:`.Bundle` is added, which allows for specification
- of groups of column expressions to a :class:`_query.Query` construct.
- The group of columns are returned as a single tuple by default. The
- behavior of :class:`.Bundle` can be overridden however to provide
- any sort of result processing to the returned row. The behavior
- of :class:`.Bundle` is also embedded into composite attributes now
- when they are used in a column-oriented :class:`_query.Query`.
-
- .. seealso::
-
- :ref:`change_2824`
-
- :ref:`migration_2824`
-
- .. change::
- :tags: bug, sql
- :tickets: 2812
-
- A rework to the way that "quoted" identifiers are handled, in that
- instead of relying upon various ``quote=True`` flags being passed around,
- these flags are converted into rich string objects with quoting information
- included at the point at which they are passed to common schema constructs
- like :class:`_schema.Table`, :class:`_schema.Column`, etc. This solves the issue
- of various methods that don't correctly honor the "quote" flag such
- as :meth:`_engine.Engine.has_table` and related methods. The :class:`.quoted_name`
- object is a string subclass that can also be used explicitly if needed;
- the object will hold onto the quoting preferences passed and will
- also bypass the "name normalization" performed by dialects that
- standardize on uppercase symbols, such as Oracle, Firebird and DB2.
- The upshot is that the "uppercase" backends can now work with force-quoted
- names, such as lowercase-quoted names and new reserved words.
-
- .. seealso::
-
- :ref:`change_2812`
-
- .. change::
- :tags: feature, orm
- :tickets: 2793
-
- The ``version_id_generator`` parameter of ``Mapper`` can now be specified
- to rely upon server generated version identifiers, using triggers
- or other database-provided versioning features, or via an optional programmatic
- value, by setting ``version_id_generator=False``.
- When using a server-generated version identifier, the ORM will use RETURNING when
- available to immediately
- load the new version value, else it will emit a second SELECT.
-
- .. change::
- :tags: feature, orm
- :tickets: 2793
-
- The ``eager_defaults`` flag of :class:`_orm.Mapper` will now allow the
- newly generated default values to be fetched using an inline
- RETURNING clause, rather than a second SELECT statement, for backends
- that support RETURNING.
-
- .. change::
- :tags: feature, core
- :tickets: 2793
-
- Added a new variant to :meth:`.UpdateBase.returning` called
- :meth:`.ValuesBase.return_defaults`; this allows arbitrary columns
- to be added to the RETURNING clause of the statement without interfering
- with the compilers usual "implicit returning" feature, which is used to
- efficiently fetch newly generated primary key values. For supporting
- backends, a dictionary of all fetched values is present at
- :attr:`_engine.ResultProxy.returned_defaults`.
-
- .. change::
- :tags: bug, mysql
-
- Improved support for the cymysql driver, supporting version 0.6.5,
- courtesy Hajime Nakagami.
-
- .. change::
- :tags: general
-
- A large refactoring of packages has reorganized
- the import structure of many Core modules as well as some aspects
- of the ORM modules. In particular ``sqlalchemy.sql`` has been broken
- out into several more modules than before so that the very large size
- of ``sqlalchemy.sql.expression`` is now pared down. The effort
- has focused on a large reduction in import cycles. Additionally,
- the system of API functions in ``sqlalchemy.sql.expression`` and
- ``sqlalchemy.orm`` has been reorganized to eliminate redundancy
- in documentation between the functions vs. the objects they produce.
-
- .. change::
- :tags: orm, feature, orm
-
- Added a new attribute :attr:`.Session.info` to :class:`.Session`;
- this is a dictionary where applications can store arbitrary
- data local to a :class:`.Session`.
- The contents of :attr:`.Session.info` can be also be initialized
- using the ``info`` argument of :class:`.Session` or
- :class:`.sessionmaker`.
-
-
- .. change::
- :tags: feature, general, py3k
- :tickets: 2161
-
- The C extensions are ported to Python 3 and will build under
- any supported CPython 2 or 3 environment.
-
- .. change::
- :tags: feature, orm
- :tickets: 2268
-
- Removal of event listeners is now implemented. The feature is
- provided via the :func:`.event.remove` function.
-
- .. seealso::
-
- :ref:`feature_2268`
-
- .. change::
- :tags: feature, orm
- :tickets: 2789
-
- The mechanism by which attribute events pass along an
- :class:`.AttributeImpl` as an "initiator" token has been changed;
- the object is now an event-specific object called :class:`.attributes.Event`.
- Additionally, the attribute system no longer halts events based
- on a matching "initiator" token; this logic has been moved to be
- specific to ORM backref event handlers, which are the typical source
- of the re-propagation of an attribute event onto subsequent append/set/remove
- operations. End user code which emulates the behavior of backrefs
- must now ensure that recursive event propagation schemes are halted,
- if the scheme does not use the backref handlers. Using this new system,
- backref handlers can now perform a
- "two-hop" operation when an object is appended to a collection,
- associated with a new many-to-one, de-associated with the previous
- many-to-one, and then removed from a previous collection. Before this
- change, the last step of removal from the previous collection would
- not occur.
-
- .. seealso::
-
- :ref:`migration_2789`
-
- .. change::
- :tags: feature, sql
- :tickets: 722
-
- Added new method to the :func:`_expression.insert` construct
- :meth:`_expression.Insert.from_select`. Given a list of columns and
- a selectable, renders ``INSERT INTO (table) (columns) SELECT ..``.
- While this feature is highlighted as part of 0.9 it is also
- backported to 0.8.3.
-
- .. seealso::
-
- :ref:`feature_722`
-
- .. change::
- :tags: feature, engine
- :tickets: 2770
-
- New events added to :class:`_events.ConnectionEvents`:
-
- * :meth:`_events.ConnectionEvents.engine_connect`
- * :meth:`_events.ConnectionEvents.set_connection_execution_options`
- * :meth:`_events.ConnectionEvents.set_engine_execution_options`
-
- .. change::
- :tags: bug, sql
- :tickets: 1765
-
- The resolution of :class:`_schema.ForeignKey` objects to their
- target :class:`_schema.Column` has been reworked to be as
- immediate as possible, based on the moment that the
- target :class:`_schema.Column` is associated with the same
- :class:`_schema.MetaData` as this :class:`_schema.ForeignKey`, rather
- than waiting for the first time a join is constructed,
- or similar. This along with other improvements allows
- earlier detection of some foreign key configuration
- issues. Also included here is a rework of the
- type-propagation system, so that
- it should be reliable now to set the type as ``None``
- on any :class:`_schema.Column` that refers to another via
- :class:`_schema.ForeignKey` - the type will be copied from the
- target column as soon as that other column is associated,
- and now works for composite foreign keys as well.
-
- .. seealso::
-
- :ref:`migration_1765`
-
- .. change::
- :tags: feature, sql
- :tickets: 2744, 2734
-
- Provided a new attribute for :class:`.TypeDecorator`
- called :attr:`.TypeDecorator.coerce_to_is_types`,
- to make it easier to control how comparisons using
- ``==`` or ``!=`` to ``None`` and boolean types goes
- about producing an ``IS`` expression, or a plain
- equality expression with a bound parameter.
-
- .. change::
- :tags: feature, pool
- :tickets: 2752
-
- Added pool logging for "rollback-on-return" and the less used
- "commit-on-return". This is enabled with the rest of pool
- "debug" logging.
-
- .. change::
- :tags: bug, orm, associationproxy
- :tickets: 2751
-
- Added additional criterion to the ==, != comparators, used with
- scalar values, for comparisons to None to also take into account
- the association record itself being non-present, in addition to the
- existing test for the scalar endpoint on the association record
- being NULL. Previously, comparing ``Cls.scalar == None`` would return
- records for which ``Cls.associated`` were present and
- ``Cls.associated.scalar`` is None, but not rows for which
- ``Cls.associated`` is non-present. More significantly, the
- inverse operation ``Cls.scalar != None`` *would* return ``Cls``
- rows for which ``Cls.associated`` was non-present.
-
- The case for ``Cls.scalar != 'somevalue'`` is also modified
- to act more like a direct SQL comparison; only rows for
- which ``Cls.associated`` is present and ``Associated.scalar``
- is non-NULL and not equal to ``'somevalue'`` are returned.
- Previously, this would be a simple ``NOT EXISTS``.
-
- Also added a special use case where you
- can call ``Cls.scalar.has()`` with no arguments,
- when ``Cls.scalar`` is a column-based value - this returns whether or
- not ``Cls.associated`` has any rows present, regardless of whether
- or not ``Cls.associated.scalar`` is NULL or not.
-
- .. seealso::
-
- :ref:`migration_2751`
-
-
- .. change::
- :tags: feature, orm
- :tickets: 2587
-
- A major change regarding how the ORM constructs joins where
- the right side is itself a join or left outer join. The ORM
- is now configured to allow simple nesting of joins of
- the form ``a JOIN (b JOIN c ON b.id=c.id) ON a.id=b.id``,
- rather than forcing the right side into a ``SELECT`` subquery.
- This should allow significant performance improvements on most
- backends, most particularly MySQL. The one database backend
- that has for many years held back this change, SQLite, is now addressed by
- moving the production of the ``SELECT`` subquery from the
- ORM to the SQL compiler; so that a right-nested join on SQLite will still
- ultimately render with a ``SELECT``, while all other backends
- are no longer impacted by this workaround.
-
- As part of this change, a new argument ``flat=True`` has been added
- to the :func:`_orm.aliased`, :meth:`_expression.Join.alias`, and
- :func:`_orm.with_polymorphic` functions, which allows an "alias" of a
- JOIN to be produced which applies an anonymous alias to each component
- table within the join, rather than producing a subquery.
-
- .. seealso::
-
- :ref:`feature_joins_09`
-
-
- .. change::
- :tags: bug, orm
- :tickets: 2369
-
- Fixed an obscure bug where the wrong results would be
- fetched when joining/joinedloading across a many-to-many
- relationship to a single-table-inheriting
- subclass with a specific discriminator value, due to "secondary"
- rows that would come back. The "secondary" and right-side
- tables are now inner joined inside of parenthesis for all
- ORM joins on many-to-many relationships so that the left->right
- join can accurately filtered. This change was made possible
- by finally addressing the issue with right-nested joins
- outlined in :ticket:`2587`.
-
- .. seealso::
-
- :ref:`feature_joins_09`
-
- .. change::
- :tags: bug, mssql, pyodbc
- :tickets: 2355
-
- Fixes to MSSQL with Python 3 + pyodbc, including that statements
- are passed correctly.
-
- .. change::
- :tags: feature, sql
- :tickets: 1068
-
- A :func:`~sqlalchemy.sql.expression.label` construct will now render as its name alone
- in an ``ORDER BY`` clause, if that label is also referred to
- in the columns clause of the select, instead of rewriting the
- full expression. This gives the database a better chance to
- optimize the evaluation of the same expression in two different
- contexts.
-
- .. seealso::
-
- :ref:`migration_1068`
-
- .. change::
- :tags: feature, firebird
- :tickets: 2504
-
- The ``fdb`` dialect is now the default dialect when
- specified without a dialect qualifier, i.e. ``firebird://``,
- per the Firebird project publishing ``fdb`` as their
- official Python driver.
-
- .. change::
- :tags: feature, general, py3k
- :tickets: 2671
-
- The codebase is now "in-place" for Python
- 2 and 3, the need to run 2to3 has been removed.
- Compatibility is now against Python 2.6 on forward.
-
- .. change::
- :tags: feature, oracle, py3k
-
- The Oracle unit tests with cx_oracle now pass
- fully under Python 3.
-
- .. change::
- :tags: bug, orm
- :tickets: 2736
-
- The "auto-aliasing" behavior of the :meth:`_query.Query.select_from`
- method has been turned off. The specific behavior is now
- available via a new method :meth:`_query.Query.select_entity_from`.
- The auto-aliasing behavior here was never well documented and
- is generally not what's desired, as :meth:`_query.Query.select_from`
- has become more oriented towards controlling how a JOIN is
- rendered. :meth:`_query.Query.select_entity_from` will also be made
- available in 0.8 so that applications which rely on the auto-aliasing
- can shift their applications to use this method.
-
- .. seealso::
-
- :ref:`migration_2736`
+++ /dev/null
-=============
-1.0 Changelog
-=============
-
-.. changelog_imports::
-
- .. include:: changelog_09.rst
- :start-line: 5
-
-
- .. include:: changelog_08.rst
- :start-line: 5
-
-
- .. include:: changelog_07.rst
- :start-line: 5
-
-
-
-.. changelog::
- :version: 1.0.19
- :released: August 3, 2017
-
- .. change::
- :tags: bug, oracle, performance, py2k
- :tickets: 4035
- :versions: 1.0.19, 1.1.13, 1.2.0b3
-
- Fixed performance regression caused by the fix for :ticket:`3937` where
- cx_Oracle as of version 5.3 dropped the ``.UNICODE`` symbol from its
- namespace, which was interpreted as cx_Oracle's "WITH_UNICODE" mode being
- turned on unconditionally, which invokes functions on the SQLAlchemy
- side which convert all strings to unicode unconditionally and causing
- a performance impact. In fact, per cx_Oracle's author the
- "WITH_UNICODE" mode has been removed entirely as of 5.1, so the expensive unicode
- conversion functions are no longer necessary and are disabled if
- cx_Oracle 5.1 or greater is detected under Python 2. The warning against
- "WITH_UNICODE" mode that was removed under :ticket:`3937` is also restored.
-
-.. changelog::
- :version: 1.0.18
- :released: July 24, 2017
-
- .. change::
- :tags: bug, tests, py3k
- :tickets: 4034
- :versions: 1.0.18, 1.1.12, 1.2.0b2
-
- Fixed issue in testing fixtures which was incompatible with a change
- made as of Python 3.6.2 involving context managers.
-
- .. change:: 3937
- :tags: bug, oracle
- :tickets: 3937
- :versions: 1.1.7
-
- A fix to cx_Oracle's WITH_UNICODE mode which was uncovered by the
- fact that cx_Oracle 5.3 now seems to hardcode this flag on in
- the build; an internal method that uses this mode wasn't using
- the correct signature.
-
-
-.. changelog::
- :version: 1.0.17
- :released: January 17, 2017
-
- .. change::
- :tags: bug, py3k
- :tickets: 3886
- :versions: 1.1.5
-
- Fixed Python 3.6 DeprecationWarnings related to escaped strings without
- the 'r' modifier, and added test coverage for Python 3.6.
-
- .. change::
- :tags: bug, orm
- :tickets: 3884
- :versions: 1.1.5
-
- Fixed bug involving joined eager loading against multiple entities
- when polymorphic inheritance is also in use which would throw
- "'NoneType' object has no attribute 'isa'". The issue was introduced
- by the fix for :ticket:`3611`.
-
-.. changelog::
- :version: 1.0.16
- :released: November 15, 2016
-
- .. change::
- :tags: bug, orm
- :tickets: 3849
- :versions: 1.1.4
-
- Fixed bug in :meth:`.Session.bulk_update_mappings` where an alternate-named
- primary key attribute would not track properly into the UPDATE statement.
-
- .. change::
- :tags: bug, mssql
- :tickets: 3810
- :versions: 1.1.0
-
- Changed the query used to get "default schema name", from one that
- queries the database principals table to using the
- "schema_name()" function, as issues have been reported that the
- former system was unavailable on the Azure Data Warehouse edition.
- It is hoped that this will finally work across all SQL Server
- versions and authentication styles.
-
- .. change::
- :tags: bug, mssql
- :tickets: 3814
- :versions: 1.1.0
-
- Updated the server version info scheme for pyodbc to use SQL Server
- SERVERPROPERTY(), rather than relying upon pyodbc.SQL_DBMS_VER, which
- continues to be unreliable particularly with FreeTDS.
-
- .. change::
- :tags: bug, orm
- :tickets: 3800
- :versions: 1.1.0
-
- Fixed bug where joined eager loading would fail for a polymorphically-
- loaded mapper, where the polymorphic_on was set to an un-mapped
- expression such as a CASE expression.
-
- .. change::
- :tags: bug, orm
- :tickets: 3798
- :versions: 1.1.0
-
- Fixed bug where the ArgumentError raised for an invalid bind
- sent to a Session via :meth:`.Session.bind_mapper`,
- :meth:`.Session.bind_table`,
- or the constructor would fail to be correctly raised.
-
- .. change::
- :tags: bug, mssql
- :tickets: 3791
- :versions: 1.1.0
-
- Added error code 20017 "unexpected EOF from the server" to the list of
- disconnect exceptions that result in a connection pool reset. Pull
- request courtesy Ken Robbins.
-
- .. change::
- :tags: bug, orm.declarative
- :tickets: 3797
- :versions: 1.1.0
-
- Fixed bug where setting up a single-table inh subclass of a joined-table
- subclass which included an extra column would corrupt the foreign keys
- collection of the mapped table, thereby interfering with the
- initialization of relationships.
-
- .. change::
- :tags: bug, orm
- :tickets: 3781
- :versions: 1.1.4
-
- Fixed bug in :meth:`.Session.bulk_save` where an UPDATE would
- not function correctly in conjunction with a mapping that
- implements a version id counter.
-
- .. 3778
-
- .. change::
- :tags: bug, orm
- :tickets: 3778
- :versions: 1.1.4
-
- Fixed bug where the :attr:`_orm.Mapper.attrs`,
- :attr:`_orm.Mapper.all_orm_descriptors` and other derived attributes would
- fail to refresh when mapper properties or other ORM constructs were
- added to the mapper/class after these accessors were first called.
-
- .. change:: 3762
- :tags: bug, mssql
- :tickets: 3762
- :versions: 1.1.4
-
- Fixed bug in pyodbc dialect (as well as in the mostly non-working
- adodbapi dialect) whereby a semicolon present in the password
- or username fields could be interpreted as a separator for another
- token; the values are now quoted when semicolons are present.
-
-.. changelog::
- :version: 1.0.15
- :released: September 1, 2016
-
- .. change::
- :tags: bug, mysql
- :tickets: 3787
- :versions: 1.1.0
-
- Added support for parsing MySQL/Connector boolean and integer
- arguments within the URL query string: connection_timeout,
- connect_timeout, pool_size, get_warnings,
- raise_on_warnings, raw, consume_results, ssl_verify_cert, force_ipv6,
- pool_reset_session, compress, allow_local_infile, use_pure.
-
- .. change::
- :tags: bug, orm
- :tickets: 3773, 3774
- :versions: 1.1.0
-
- Fixed bug in subquery eager loading where a subqueryload
- of an "of_type()" object linked to a second subqueryload of a plain
- mapped class, or a longer chain of several "of_type()" attributes,
- would fail to link the joins correctly.
-
- .. change::
- :tags: bug, sql
- :tickets: 3755
- :versions: 1.1.0
-
- Fixed bug in :class:`_schema.Table` where the internal method
- ``_reset_exported()`` would corrupt the state of the object. This
- method is intended for selectable objects and is called by the ORM
- in some cases; an erroneous mapper configuration would could lead the
- ORM to call this on a :class:`_schema.Table` object.
-
- .. change::
- :tags: bug, ext
- :tickets: 3743
- :versions: 1.1.0b3
-
- Fixed bug in ``sqlalchemy.ext.baked`` where the unbaking of a
- subquery eager loader query would fail due to a variable scoping
- issue, when multiple subquery loaders were involved. Pull request
- courtesy Mark Hahnenberg.
-
-.. changelog::
- :version: 1.0.14
- :released: July 6, 2016
-
- .. change::
- :tags: bug, postgresql
- :tickets: 3739
- :versions: 1.1.0b3
-
- Fixed bug whereby :class:`.TypeDecorator` and :class:`.Variant`
- types were not deeply inspected enough by the PostgreSQL dialect
- to determine if SMALLSERIAL or BIGSERIAL needed to be rendered
- rather than SERIAL.
-
- .. change::
- :tags: bug, oracle
- :tickets: 3741
- :versions: 1.1.0b3
-
- Fixed bug in :paramref:`.Select.with_for_update.of`, where the Oracle
- "rownum" approach to LIMIT/OFFSET would fail to accommodate for the
- expressions inside the "OF" clause, which must be stated at the topmost
- level referring to expression within the subquery. The expressions are
- now added to the subquery if needed.
-
- .. change::
- :tags: bug, sql
- :tickets: 3735
- :versions: 1.1.0b2
-
- Fixed issue in SQL math negation operator where the type of the
- expression would no longer be the numeric type of the original.
- This would cause issues where the type determined result set
- behaviors.
-
- .. change::
- :tags: bug, sql
- :tickets: 3728
- :versions: 1.1.0b2
-
- Fixed bug whereby the ``__getstate__`` / ``__setstate__``
- methods for sqlalchemy.util.Properties were
- non-working due to the transition in the 1.0 series to ``__slots__``.
- The issue potentially impacted some third-party applications.
- Pull request courtesy Pieter Mulder.
-
- .. change::
- :tags: bug, sql
- :tickets: 3724
-
- :meth:`_expression.FromClause.count` is pending deprecation for 1.1. This function
- makes use of an arbitrary column in the table and is not reliable;
- for Core use, ``func.count()`` should be preferred.
-
- .. change::
- :tags: bug, sql
- :tickets: 3722
-
- Fixed bug in :class:`_expression.CTE` structure which would cause it to not
- clone properly when a union was used, as is common in a recursive
- CTE. The improper cloning would cause errors when the CTE is used
- in various ORM contexts such as that of a :func:`.column_property`.
-
- .. change::
- :tags: bug, sql
- :tickets: 3721
-
- Fixed bug whereby :meth:`_schema.Table.tometadata` would make a duplicate
- :class:`.UniqueConstraint` for each :class:`_schema.Column` object that
- featured the ``unique=True`` parameter.
-
- .. change::
- :tags: bug, engine, postgresql
- :tickets: 3716
-
- Fixed bug in cross-schema foreign key reflection in conjunction
- with the :paramref:`_schema.MetaData.schema` argument, where a referenced
- table that is present in the "default" schema would fail since there
- would be no way to indicate a :class:`_schema.Table` that has "blank" for
- a schema. The special symbol :attr:`_schema.BLANK_SCHEMA` has been
- added as an available value for :paramref:`_schema.Table.schema` and
- :paramref:`.Sequence.schema`, indicating that the schema name
- should be forced to be ``None`` even if :paramref:`_schema.MetaData.schema`
- is specified.
-
- .. change::
- :tags: bug, examples
- :tickets: 3704
-
- Fixed a regression that occurred in the
- examples/vertical/dictlike-polymorphic.py example which prevented it
- from running.
-
-.. changelog::
- :version: 1.0.13
- :released: May 16, 2016
-
- .. change::
- :tags: bug, orm
- :tickets: 3700
-
- Fixed bug in "evaluate" strategy of :meth:`_query.Query.update` and
- :meth:`_query.Query.delete` which would fail to accommodate a bound
- parameter with a "callable" value, as which occurs when filtering
- by a many-to-one equality expression along a relationship.
-
- .. change::
- :tags: bug, postgresql
- :tickets: 3715
-
- Added disconnect detection support for the error string
- "SSL error: decryption failed or bad record mac". Pull
- request courtesy Iuri de Silvio.
-
- .. change::
- :tags: bug, mssql
- :tickets: 3711
-
- Fixed bug where by ROW_NUMBER OVER clause applied for OFFSET
- selects in SQL Server would inappropriately substitute a plain column
- from the local statement that overlaps with a label name used by
- the ORDER BY criteria of the statement.
-
- .. change::
- :tags: bug, orm
- :tickets: 3710
-
- Fixed bug whereby the event listeners used for backrefs could
- be inadvertently applied multiple times, when using a deep class
- inheritance hierarchy in conjunction with multiple mapper configuration
- steps.
-
- .. change::
- :tags: bug, orm
- :tickets: 3706
-
- Fixed bug whereby passing a :func:`_expression.text` construct to the
- :meth:`_query.Query.group_by` method would raise an error, instead
- of interpreting the object as a SQL fragment.
-
- .. change::
- :tags: bug, oracle
- :tickets: 3705
-
- Fixed a bug in the cx_Oracle connect process that caused a TypeError
- when the either the user, password or dsn was empty. This prevented
- external authentication to Oracle databases, and prevented connecting
- to the default dsn. The connect string oracle:// now logs into the
- default dsn using the Operating System username, equivalent to
- connecting using '/' with sqlplus.
-
- .. change::
- :tags: bug, oracle
- :tickets: 3699
-
- Fixed a bug in the result proxy used mainly by Oracle when binary and
- other LOB types are in play, such that when query / statement caching
- were used, the type-level result processors, notably that required by
- the binary type itself but also any other processor, would become lost
- after the first run of the statement due to it being removed from the
- cached result metadata.
-
- .. change::
- :tags: bug, examples
- :tickets: 3698
-
- Changed the "directed graph" example to no longer consider
- integer identifiers of nodes as significant; the "higher" / "lower"
- references now allow mutual edges in both directions.
-
- .. change::
- :tags: bug, sql
- :tickets: 3690
-
- Fixed bug where when using ``case_sensitive=False`` with an
- :class:`_engine.Engine`, the result set would fail to correctly accommodate
- for duplicate column names in the result set, causing an error
- when the statement is executed in 1.0, and preventing the
- "ambiguous column" exception from functioning in 1.1.
-
- .. change::
- :tags: bug, sql
- :tickets: 3682
-
- Fixed bug where the negation of an EXISTS expression would not
- be properly typed as boolean in the result, and also would fail to be
- anonymously aliased in a SELECT list as is the case with a
- non-negated EXISTS construct.
-
- .. change::
- :tags: bug, sql
- :tickets: 3666
-
- Fixed bug where "unconsumed column names" exception would fail to
- be raised in the case where :meth:`_expression.Insert.values` were called
- with a list of parameter mappings, instead of a single mapping
- of parameters. Pull request courtesy Athena Yao.
-
- .. change::
- :tags: bug, orm
- :tickets: 3663
-
- Anonymous labeling is applied to a :attr:`.func` construct that is
- passed to :func:`.column_property`, so that if the same attribute
- is referred to as a column expression twice the names are de-duped,
- thus avoiding "ambiguous column" errors. Previously, the
- ``.label(None)`` would need to be applied in order for the name
- to be de-anonymized.
-
- .. change::
- :tags: bug, py3k
- :tickets: 3660
-
- Fixed bug in "to_list" conversion where a single bytes object
- would be turned into a list of individual characters. This would
- impact among other things using the :meth:`_query.Query.get` method
- on a primary key that's a bytes object.
-
- .. change::
- :tags: bug, orm
- :tickets: 3658
-
- Fixed regression appearing in the 1.0 series in ORM loading where the
- exception raised for an expected column missing would incorrectly
- be a ``NoneType`` error, rather than the expected
- :class:`.NoSuchColumnError`.
-
- .. change::
- :tags: bug, mssql, oracle
- :tickets: 3657
-
- Fixed regression appearing in the 1.0 series which would cause the Oracle
- and SQL Server dialects to incorrectly account for result set columns
- when these dialects would wrap a SELECT in a subquery in order to
- provide LIMIT/OFFSET behavior, and the original SELECT statement
- referred to the same column multiple times, such as a column and
- a label of that same column. This issue is related
- to :ticket:`3658` in that when the error occurred, it would also
- cause a ``NoneType`` error, rather than reporting that it couldn't
- locate a column.
-
-.. changelog::
- :version: 1.0.12
- :released: February 15, 2016
-
- .. change::
- :tags: bug, orm
- :tickets: 3647
-
- Fixed bug in :meth:`.Session.merge` where an object with a composite
- primary key that has values for some but not all of the PK fields
- would emit a SELECT statement leaking the internal NEVER_SET symbol
- into the query, rather than detecting that this object does not have
- a searchable primary key and no SELECT should be emitted.
-
- .. change::
- :tags: bug, postgresql
- :tickets: 3644
-
- Fixed bug in :func:`_expression.text` construct where a double-colon
- expression would not escape properly, e.g. ``some\:\:expr``, as is most
- commonly required when rendering PostgreSQL-style CAST expressions.
-
- .. change::
- :tags: bug, sql
- :tickets: 3643
-
- Fixed issue where the "literal_binds" flag was not propagated
- for :func:`_expression.insert`, :func:`_expression.update` or
- :func:`_expression.delete` constructs when compiled to string
- SQL. Pull request courtesy Tim Tate.
-
- .. change::
- :tags: bug, oracle, jython
- :tickets: 3621
-
- Fixed a small issue in the Jython Oracle compiler involving the
- rendering of "RETURNING" which allows this currently
- unsupported/untested dialect to work rudimentarily with the 1.0 series.
- Pull request courtesy Carlos Rivas.
-
- .. change::
- :tags: bug, sql
- :tickets: 3642
-
- Fixed issue where inadvertent use of the Python ``__contains__``
- override with a column expression (e.g. by using ``'x' in col``)
- would cause an endless loop in the case of an ARRAY type, as Python
- defers this to ``__getitem__`` access which never raises for this
- type. Overall, all use of ``__contains__`` now raises
- NotImplementedError.
-
- .. change::
- :tags: bug, engine, mysql
- :tickets: 2696
-
- Revisiting :ticket:`2696`, first released in 1.0.10, which attempts to
- work around Python 2's lack of exception context reporting by emitting
- a warning for an exception that was interrupted by a second exception
- when attempting to roll back the already-failed transaction; this
- issue continues to occur for MySQL backends in conjunction with a
- savepoint that gets unexpectedly lost, which then causes a
- "no such savepoint" error when the rollback is attempted, obscuring
- what the original condition was.
-
- The approach has been generalized to the Core "safe
- reraise" function which takes place across the ORM and Core in any
- place that a transaction is being rolled back in response to an error
- which occurred trying to commit, including the context managers
- provided by :class:`.Session` and :class:`_engine.Connection`, and taking
- place for operations such as a failure on "RELEASE SAVEPOINT".
- Previously, the fix was only in place for a specific path within
- the ORM flush/commit process; it now takes place for all transactional
- context managers as well.
-
- .. change::
- :tags: bug, sql
- :tickets: 3632
-
- Fixed bug in :class:`_schema.Table` metadata construct which appeared
- around the 0.9 series where adding columns to a :class:`_schema.Table`
- that was unpickled would fail to correctly establish the
- :class:`_schema.Column` within the 'c' collection, leading to issues in
- areas such as ORM configuration. This could impact use cases such
- as ``extend_existing`` and others.
-
- .. change::
- :tags: bug, py3k
- :tickets: 3625
-
- Fixed bug where some exception re-raise scenarios would attach
- the exception to itself as the "cause"; while the Python 3 interpreter
- is OK with this, it could cause endless loops in iPython.
-
- .. change::
- :tags: bug, mssql
- :tickets: 3624
-
- Fixed the syntax of the :func:`.extract` function when used on
- MSSQL against a datetime value; the quotes around the keyword
- are removed. Pull request courtesy Guillaume Doumenc.
-
- .. change::
- :tags: bug, orm
- :tickets: 3623
-
- Fixed regression since 0.9 where the 0.9 style loader options
- system failed to accommodate for multiple :func:`.undefer_group`
- loader options in a single query. Multiple :func:`.undefer_group`
- options will now be taken into account even against the same
- entity.
-
- .. change::
- :tags: bug, mssql, firebird
- :tickets: 3622
-
- Fixed 1.0 regression where the eager fetch of cursor.rowcount was
- no longer called for an UPDATE or DELETE statement emitted via plain
- text or via the :func:`_expression.text` construct, affecting those drivers
- that erase cursor.rowcount once the cursor is closed such as SQL
- Server ODBC and Firebird drivers.
-
-
-.. changelog::
- :version: 1.0.11
- :released: December 22, 2015
-
- .. change::
- :tags: bug, mysql
- :tickets: 3613
-
- An adjustment to the regular expression used to parse MySQL views,
- such that we no longer assume the "ALGORITHM" keyword is present in
- the reflected view source, as some users have reported this not being
- present in some Amazon RDS environments.
-
- .. change::
- :tags: bug, mysql
-
- Added new reserved words for MySQL 5.7 to the MySQL dialect,
- including 'generated', 'optimizer_costs', 'stored', 'virtual'.
- Pull request courtesy Hanno Schlichting.
-
- .. change::
- :tags: bug, ext
- :tickets: 3605
-
- Further fixes to :ticket:`3605`, pop method on :class:`.MutableDict`,
- where the "default" argument was not included.
-
- .. change::
- :tags: bug, ext
- :tickets: 3612
-
- Fixed bug in baked loader system where the systemwide monkeypatch
- for setting up baked lazy loaders would interfere with other
- loader strategies that rely on lazy loading as a fallback, e.g.
- joined and subquery eager loaders, leading to ``IndexError``
- exceptions at mapper configuration time.
-
- .. change::
- :tags: bug, orm
- :tickets: 3611
-
- Fixed regression caused in 1.0.10 by the fix for :ticket:`3593` where
- the check added for a polymorphic joinedload from a
- poly_subclass->class->poly_baseclass connection would fail for the
- scenario of class->poly_subclass->class.
-
- .. change::
- :tags: bug, orm
- :tickets: 3610
-
- Fixed bug where :meth:`.Session.bulk_update_mappings` and related
- would not bump a version id counter when in use. The experience
- here is still a little rough as the original version id is required
- in the given dictionaries and there's not clean error reporting
- on that yet.
-
- .. change::
- :tags: bug, sql
- :tickets: 3609
-
- Fixed bug in :meth:`_expression.Update.return_defaults` which would cause all
- insert-default holding columns not otherwise included in the SET
- clause (such as primary key cols) to get rendered into the RETURNING
- even though this is an UPDATE.
-
- .. change::
- :tags: bug, orm
- :tickets: 3609
-
- Major fixes to the :paramref:`_orm.Mapper.eager_defaults` flag, this
- flag would not be honored correctly in the case that multiple
- UPDATE statements were to be emitted, either as part of a flush
- or a bulk update operation. Additionally, RETURNING
- would be emitted unnecessarily within update statements.
-
- .. change::
- :tags: bug, orm
- :tickets: 3606
-
- Fixed bug where use of the :meth:`_query.Query.select_from` method would
- cause a subsequent call to the :meth:`_query.Query.with_parent` method to
- fail.
-
-.. changelog::
- :version: 1.0.10
- :released: December 11, 2015
-
- .. change::
- :tags: bug, ext
- :tickets: 3605
-
- Added support for the ``dict.pop()`` and ``dict.popitem()`` methods
- to the :class:`.mutable.MutableDict` class.
-
- .. change::
- :tags: change, tests
-
- The ORM and Core tutorials, which have always been in doctest format,
- are now exercised within the normal unit test suite in both Python
- 2 and Python 3.
-
- .. change::
- :tags: bug, sql
- :tickets: 3603
-
- Fixed issue within the :meth:`_expression.Insert.from_select` construct whereby
- the :class:`_expression.Select` construct would have its ``._raw_columns``
- collection mutated in-place when compiling the :class:`_expression.Insert`
- construct, when the target :class:`_schema.Table` has Python-side defaults.
- The :class:`_expression.Select` construct would compile standalone with the
- erroneous column present subsequent to compilation of the
- :class:`_expression.Insert`, and the :class:`_expression.Insert` statement itself would
- fail on a second compile attempt due to duplicate bound parameters.
-
- .. change::
- :tags: bug, mysql
- :tickets: 3602
-
- Fixed bug in MySQL reflection where the "fractional sections portion"
- of the :class:`.mysql.DATETIME`, :class:`.mysql.TIMESTAMP` and
- :class:`.mysql.TIME` types would be incorrectly placed into the
- ``timezone`` attribute, which is unused by MySQL, instead of the
- ``fsp`` attribute.
-
- .. change::
- :tags: bug, orm
- :tickets: 3599
-
- Fixed issue where post_update on a many-to-one relationship would
- fail to emit an UPDATE in the case where the attribute were set to
- None and not previously loaded.
-
- .. change::
- :tags: bug, sql, postgresql
- :tickets: 3598
-
- Fixed bug where CREATE TABLE with a no-column table, but a constraint
- such as a CHECK constraint would render an erroneous comma in the
- definition; this scenario can occur such as with a PostgreSQL
- INHERITS table that has no columns of its own.
-
- .. change::
- :tags: bug, mssql
- :tickets: 3585
-
-
- Added the error "20006: Write to the server failed" to the list
- of disconnect errors for the pymssql driver, as this has been observed
- to render a connection unusable.
-
- .. change::
- :tags: bug, postgresql
- :tickets: 3573
-
-
- Fixed issue where the "FOR UPDATE OF" PostgreSQL-specific SELECT
- modifier would fail if the referred table had a schema qualifier;
- PG needs the schema name to be omitted. Pull request courtesy
- Diana Clarke.
-
- .. change::
- :tags: bug, postgresql
-
-
- Fixed bug where some varieties of SQL expression passed to the
- "where" clause of :class:`_postgresql.ExcludeConstraint` would fail
- to be accepted correctly. Pull request courtesy aisch.
-
- .. change::
- :tags: bug, orm, declarative
-
-
- Fixed bug where in Py2K a unicode literal would not be accepted as the
- string name of a class or other argument within declarative using
- :func:`.backref` on :func:`_orm.relationship`. Pull request courtesy
- Nils Philippsen.
-
- .. change::
- :tags: bug, mssql
-
- A descriptive ValueError is now raised in the event that SQL server
- returns an invalid date or time format from a DATE or TIME
- column, rather than failing with a NoneType error. Pull request
- courtesy Ed Avis.
-
- .. change::
- :tags: bug, py3k
-
- Updates to internal getargspec() calls, some py36-related
- fixture updates, and alterations to two iterators to "return" instead
- of raising StopIteration, to allow tests to pass without
- errors or warnings on Py3.5, Py3.6, pull requests courtesy
- Jacob MacDonald, Luri de Silvio, and Phil Jones.
-
- .. change::
- :tags: bug, ext
- :tickets: 3597
-
- Fixed an issue in baked queries where the .get() method, used either
- directly or within lazy loads, didn't consider the mapper's "get clause"
- as part of the cache key, causing bound parameter mismatches if the
- clause got re-generated. This clause is cached by mappers
- on the fly but in highly concurrent scenarios may be generated more
- than once when first accessed.
-
- .. change::
- :tags: feature, sql
-
- Added support for parameter-ordered SET clauses in an UPDATE
- statement. This feature is available by passing the
- :paramref:`~.sqlalchemy.sql.expression.update.preserve_parameter_order`
- flag either to the core :class:`_expression.Update` construct or alternatively
- adding it to the :paramref:`.Query.update.update_args` dictionary at
- the ORM-level, also passing the parameters themselves as a list of 2-tuples.
- Thanks to Gorka Eguileor for implementation and tests.
-
- .. seealso::
-
- :ref:`tutorial_parameter_ordered_updates`
-
- .. change::
- :tags: bug, orm
- :tickets: 3593
-
- Fixed bug which is actually a regression that occurred between
- versions 0.8.0 and 0.8.1, due :ticket:`2714`. The case where
- joined eager loading needs to join out over a subclass-bound
- relationship when "with_polymorphic" were also used would fail
- to join from the correct entity.
-
- .. change::
- :tags: bug, orm
- :tickets: 3592
-
- Fixed joinedload bug which would occur when a. the query includes
- limit/offset criteria that forces a subquery b. the relationship
- uses "secondary" c. the primaryjoin of the relationship refers to
- a column that is either not part of the primary key, or is a PK
- col in a joined-inheritance subclass table that is under a different
- attribute name than the parent table's primary key column d. the
- query defers the columns that are present in the primaryjoin, typically
- via not being included in load_only(); the necessary column(s) would
- not be present in the subquery and produce invalid SQL.
-
- .. change::
- :tags: bug, orm
- :tickets: 2696
-
- A rare case which occurs when a :meth:`.Session.rollback` fails in the
- scope of a :meth:`.Session.flush` operation that's raising an
- exception, as has been observed in some MySQL SAVEPOINT cases, prevents
- the original database exception from being observed when it was
- emitted during flush, but only on Py2K because Py2K does not support
- exception chaining; on Py3K the originating exception is chained. As
- a workaround, a warning is emitted in this specific case showing at
- least the string message of the original database error before we
- proceed to raise the rollback-originating exception.
-
- .. change::
- :tags: bug, postgresql
- :tickets: 3571
-
- Fixed the ``.python_type`` attribute of :class:`_postgresql.INTERVAL`
- to return ``datetime.timedelta`` in the same way as that of
- :obj:`.types.Interval.python_type`, rather than raising
- ``NotImplementedError``.
-
- .. change::
- :tags: bug, mssql
-
-
- Fixed issue where DDL generated for the MSSQL types DATETIME2,
- TIME and DATETIMEOFFSET with a precision of "zero" would not generate
- the precision field. Pull request courtesy Jacobo de Vera.
-
-
-.. changelog::
- :version: 1.0.9
- :released: October 20, 2015
-
- .. change::
- :tags: bug, orm, postgresql
- :tickets: 3556
-
- Fixed regression in 1.0 where new feature of using "executemany"
- for UPDATE statements in the ORM (e.g. :ref:`feature_updatemany`)
- would break on PostgreSQL and other RETURNING backends
- when using server-side version generation
- schemes, as the server side value is retrieved via RETURNING which
- is not supported with executemany.
-
- .. change::
- :tags: feature, ext
- :tickets: 3551
-
- Added the :paramref:`.AssociationProxy.info` parameter to the
- :class:`.AssociationProxy` constructor, to suit the
- :attr:`.AssociationProxy.info` accessor that was added in
- :ticket:`2971`. This is possible because :class:`.AssociationProxy`
- is constructed explicitly, unlike a hybrid which is constructed
- implicitly via the decorator syntax.
-
- .. change::
- :tags: bug, oracle
- :tickets: 3548
-
- Fixed bug in Oracle dialect where reflection of tables and other
- symbols with names quoted to force all-lower-case would not be
- identified properly in reflection queries. The :class:`.quoted_name`
- construct is now applied to incoming symbol names that detect as
- forced into all-lower-case within the "name normalize" process.
-
- .. change::
- :tags: feature, orm
-
- Added new method :meth:`_query.Query.one_or_none`; same as
- :meth:`_query.Query.one` but returns None if no row found. Pull request
- courtesy esiegerman.
-
- .. change::
- :tags: bug, orm
- :tickets: 3539
-
- Fixed rare TypeError which could occur when stringifying certain
- kinds of internal column loader options within internal logging.
-
- .. change::
- :tags: bug, orm
- :tickets: 3525
-
- Fixed bug in :meth:`.Session.bulk_save_objects` where a mapped
- column that had some kind of "fetch on update" value and was not
- locally present in the given object would cause an AttributeError
- within the operation.
-
- .. change::
- :tags: bug, sql
- :tickets: 3520
-
- Fixed regression in 1.0-released default-processor for multi-VALUES
- insert statement, :ticket:`3288`, where the column type for the
- default-holding column would not be propagated to the compiled
- statement in the case where the default was being used,
- leading to bind-level type handlers not being invoked.
-
- .. change::
- :tags: bug, examples
-
-
- Fixed two issues in the "history_meta" example where history tracking
- could encounter empty history, and where a column keyed to an alternate
- attribute name would fail to track properly. Fixes courtesy
- Alex Fraser.
-
- .. change::
- :tags: bug, orm
- :tickets: 3510
-
-
- Fixed 1.0 regression where the "noload" loader strategy would fail
- to function for a many-to-one relationship. The loader used an
- API to place "None" into the dictionary which no longer actually
- writes a value; this is a side effect of :ticket:`3061`.
-
- .. change::
- :tags: bug, sybase
- :tickets: 3508, 3509
-
-
- Fixed two issues regarding Sybase reflection, allowing tables
- without primary keys to be reflected as well as ensured that
- a SQL statement involved in foreign key detection is pre-fetched up
- front to avoid driver issues upon nested queries. Fixes here
- courtesy Eugene Zapolsky; note that we cannot currently test
- Sybase to locally verify these changes.
-
- .. change::
- :tags: bug, postgresql
-
-
- An adjustment to the new PostgreSQL feature of reflecting storage
- options and USING of :ticket:`3455` released in 1.0.6,
- to disable the feature for PostgreSQL versions < 8.2 where the
- ``reloptions`` column is not provided; this allows Amazon Redshift
- to again work as it is based on an 8.0.x version of PostgreSQL.
- Fix courtesy Pete Hollobon.
-
-
-.. changelog::
- :version: 1.0.8
- :released: July 22, 2015
-
- .. change::
- :tags: bug, misc
- :tickets: 3494
-
- Fixed an issue where a particular base class within utils
- didn't implement ``__slots__``, and therefore meant all subclasses
- of that class didn't either, negating the rationale for ``__slots__``
- to be in use. Didn't cause any issue except on IronPython
- which apparently does not implement ``__slots__`` behavior compatibly
- with cPython.
-
-
-.. changelog::
- :version: 1.0.7
- :released: July 20, 2015
-
- .. change::
- :tags: feature, sql
- :tickets: 3459
-
- Added a :meth:`_expression.ColumnElement.cast` method which performs the same
- purpose as the standalone :func:`_expression.cast` function. Pull
- request courtesy Sebastian Bank.
-
- .. change::
- :tags: bug, engine
- :tickets: 3481
-
- Fixed regression where new methods on :class:`_engine.ResultProxy` used
- by the ORM :class:`_query.Query` object (part of the performance
- enhancements of :ticket:`3175`) would not raise the "this result
- does not return rows" exception in the case where the driver
- (typically MySQL) fails to generate cursor.description correctly;
- an AttributeError against NoneType would be raised instead.
-
- .. change::
- :tags: bug, engine
- :tickets: 3483
-
- Fixed regression where :meth:`_engine.ResultProxy.keys` would return
- un-adjusted internal symbol names for "anonymous" labels, which
- are the "foo_1" types of labels we see generated for SQL functions
- without labels and similar. This was a side effect of the
- performance enhancements implemented as part of #918.
-
-
- .. change::
- :tags: bug, sql
- :tickets: 3490
-
- Fixed bug where coercion of literal ``True`` or ``False`` constant
- in conjunction with :func:`.and_` or :func:`.or_` would fail
- with an AttributeError.
-
- .. change::
- :tags: bug, sql
- :tickets: 3485
-
- Fixed potential issue where a custom subclass
- of :class:`.FunctionElement` or other column element that incorrectly
- states 'None' or any other invalid object as the ``.type``
- attribute will report this exception instead of recursion overflow.
-
- .. change::
- :tags: bug, sql
-
- Fixed bug where the modulus SQL operator wouldn't work in reverse
- due to a missing ``__rmod__`` method. Pull request courtesy
- dan-gittik.
-
- .. change::
- :tags: feature, schema
-
- Added support for the MINVALUE, MAXVALUE, NO MINVALUE, NO MAXVALUE,
- and CYCLE arguments for CREATE SEQUENCE as supported by PostgreSQL
- and Oracle. Pull request courtesy jakeogh.
-
- .. change::
- :tags: bug, orm, declarative
- :tickets: 3480
-
- Fixed bug in :class:`.AbstractConcreteBase` extension where
- a column setup on the ABC base which had a different attribute
- name vs. column name would not be correctly mapped on the final
- base class. The failure on 0.9 would be silent whereas on
- 1.0 it raised an ArgumentError, so may not have been noticed
- prior to 1.0.
-
- .. change::
- :tags: bug, orm
- :tickets: 3469
-
- Fixed 1.0 regression where value objects that override
- ``__eq__()`` to return a non-boolean-capable object, such as
- some geoalchemy types as well as numpy types, were being tested
- for ``bool()`` during a unit of work update operation, where in
- 0.9 the return value of ``__eq__()`` was tested against "is True"
- to guard against this.
-
- .. change::
- :tags: bug, orm
- :tickets: 3468
-
- Fixed 1.0 regression where a "deferred" attribute would not populate
- correctly if it were loaded within the "optimized inheritance load",
- which is a special SELECT emitted in the case of joined table
- inheritance used to populate expired or unloaded attributes against
- a joined table without loading the base table. This is related to
- the fact that SQLA 1.0 no longer guesses about loading deferred
- columns and must be directed explicitly.
-
- .. change::
- :tags: bug, orm
- :tickets: 3466
-
- Fixed 1.0 regression where the "parent entity" of a synonym-
- mapped attribute on top of an :func:`.aliased` object would
- resolve to the original mapper, not the :func:`.aliased`
- version of it, thereby causing problems for a :class:`_query.Query`
- that relies on this attribute (e.g. it's the only representative
- attribute given in the constructor) to figure out the correct FROM
- clause for the query.
-
-.. changelog::
- :version: 1.0.6
- :released: June 25, 2015
-
- .. change::
- :tags: bug, orm
- :tickets: 3465
-
- Fixed a major regression in the 1.0 series where the version_id_counter
- feature would cause an object's version counter to be incremented
- when there was no net change to the object's row, but instead an object
- related to it via relationship (e.g. typically many-to-one)
- were associated or de-associated with it, resulting in an UPDATE
- statement that updates the object's version counter and nothing else.
- In the use case where the relatively recent "server side" and/or
- "programmatic/conditional" version counter feature were used
- (e.g. setting version_id_generator to False), the bug could cause an
- UPDATE without a valid SET clause to be emitted.
-
- .. change::
- :tags: bug, mssql
- :tickets: 3464
-
- Fixed issue when using :class:`_types.VARBINARY` type in conjunction with
- an INSERT of NULL + pyodbc; pyodbc requires a special
- object be passed in order to persist NULL. As the :class:`_types.VARBINARY`
- type is now usually the default for :class:`.LargeBinary` due to
- :ticket:`3039`, this issue is partially a regression in 1.0.
- The pymssql driver appears to be unaffected.
-
- .. change::
- :tags: bug, postgresql, pypy
- :tickets: 3439
-
- Re-fixed this issue first released in 1.0.5 to fix psycopg2cffi
- JSONB support once again, as they suddenly
- switched on unconditional decoding of JSONB types in version 2.7.1.
- Version detection now specifies 2.7.1 as where we should expect
- the DBAPI to do json encoding for us.
-
- .. change::
- :tags: feature, postgresql
- :tickets: 3455
-
- Added support for storage parameters under CREATE INDEX, using
- a new keyword argument ``postgresql_with``. Also added support for
- reflection to support both the ``postgresql_with`` flag as well
- as the ``postgresql_using`` flag, which will now be set on
- :class:`.Index` objects that are reflected, as well present
- in a new "dialect_options" dictionary in the result of
- :meth:`_reflection.Inspector.get_indexes`. Pull request courtesy Pete Hollobon.
-
- .. seealso::
-
- :ref:`postgresql_index_storage`
-
- .. change::
- :tags: bug, orm
- :tickets: 3462
-
- Fixed 1.0 regression where the enhanced behavior of single-inheritance
- joins of :ticket:`3222` takes place inappropriately
- for a JOIN along explicit join criteria with a single-inheritance
- subclass that does not make use of any discriminator, resulting
- in an additional "AND NULL" clause.
-
- .. change::
- :tags: bug, postgresql
- :tickets: 3454
-
- Repaired the :class:`.ExcludeConstraint` construct to support common
- features that other objects like :class:`.Index` now do, that
- the column expression may be specified as an arbitrary SQL
- expression such as :obj:`_expression.cast` or :obj:`_expression.text`.
-
- .. change::
- :tags: feature, postgresql
-
- Added new execution option ``max_row_buffer`` which is interpreted
- by the psycopg2 dialect when the ``stream_results`` option is
- used, which sets a limit on the size of the row buffer that may be
- allocated. This value is also provided based on the integer
- value sent to :meth:`_query.Query.yield_per`. Pull request courtesy
- mcclurem.
-
- .. change::
- :tags: bug, orm
- :tickets: 3451
-
- Fixed bug in new :meth:`.Session.bulk_update_mappings` feature where
- the primary key columns used in the WHERE clause to locate the row
- would also be included in the SET clause, setting their value to
- themselves unnecessarily. Pull request courtesy Patrick Hayes.
-
- .. change::
- :tags: bug, orm
- :tickets: 3448
-
- Fixed an unexpected-use regression whereby custom
- :class:`.types.TypeEngine.Comparator` objects that made use of the
- ``__clause_element__()`` method and returned an object that was an
- ORM-mapped :class:`.InstrumentedAttribute` and not explicitly a
- :class:`_expression.ColumnElement` would fail to be correctly handled when passed
- as an expression to :meth:`.Session.query`. The logic in 0.9 happened
- to succeed on this, so this use case is now supported.
-
- .. change::
- :tags: bug, sql
- :tickets: 3445
-
- Fixed a bug where clause adaption as applied to a :class:`.Label`
- object would fail to accommodate the labeled SQL expression
- in all cases, such that any SQL operation that made use of
- :meth:`.Label.self_group` would use the original unadapted
- expression. One effect of this would be that an ORM :func:`.aliased`
- construct would not fully accommodate attributes mapped by
- :obj:`.column_property`, such that the un-aliased table could
- leak out when the property were used in some kinds of SQL
- comparisons.
-
- .. change::
- :tags: bug, documentation
- :tickets: 2077
-
- Fixed an internal "memoization" routine for method types such
- that a Python descriptor is no longer used; repairs inspectability
- of these methods including support for Sphinx documentation.
-
-.. changelog::
- :version: 1.0.5
- :released: June 7, 2015
-
- .. change::
- :tags: feature, engine
-
- Added new engine event :meth:`_events.ConnectionEvents.engine_disposed`.
- Called after the :meth:`_engine.Engine.dispose` method is called.
-
- .. change::
- :tags: bug, postgresql, pypy
- :tickets: 3439
-
- Repaired some typing and test issues related to the pypy
- psycopg2cffi dialect, in particular that the current 2.7.0 version
- does not have native support for the JSONB type. The version detection
- for psycopg2 features has been tuned into a specific sub-version
- for psycopg2cffi. Additionally, test coverage has been enabled
- for the full series of psycopg2 features under psycopg2cffi.
-
- .. change::
- :tags: feature, ext
-
- Added support for ``*args`` to be passed to the baked query
- initial callable, in the same way that ``*args`` are supported
- for the :meth:`.BakedQuery.add_criteria` and
- :meth:`.BakedQuery.with_criteria` methods. Initial PR courtesy
- Naoki INADA.
-
- .. change::
- :tags: bug, engine
- :tickets: 3435
-
- Fixed bug where known boolean values used by
- :func:`.engine_from_config` were not being parsed correctly;
- these included ``pool_threadlocal`` and the psycopg2 argument
- ``use_native_unicode``.
-
- .. change::
- :tags: bug, mssql
- :tickets: 3424, 3430
-
- Added a new dialect flag to the MSSQL dialect
- ``legacy_schema_aliasing`` which when set to False will disable a
- very old and obsolete behavior, that of the compiler's
- attempt to turn all schema-qualified table names into alias names,
- to work around old and no longer locatable issues where SQL
- server could not parse a multi-part identifier name in all
- circumstances. The behavior prevented more
- sophisticated statements from working correctly, including those which
- use hints, as well as CRUD statements that embed correlated SELECT
- statements. Rather than continue to repair the feature to work
- with more complex statements, it's better to just disable it
- as it should no longer be needed for any modern SQL server
- version. The flag defaults to True for the 1.0.x series, leaving
- current behavior unchanged for this version series. In the 1.1
- series, it will default to False. For the 1.0 series,
- when not set to either value explicitly, a warning is emitted
- when a schema-qualified table is first used in a statement, which
- suggests that the flag be set to False for all modern SQL Server
- versions.
-
- .. seealso::
-
- :ref:`legacy_schema_rendering`
-
- .. change::
- :tags: feature, engine
- :tickets: 3379
-
- Adjustments to the engine plugin hook, such that the
- :meth:`.URL.get_dialect` method will continue to return the
- ultimate :class:`.Dialect` object when a dialect plugin is used,
- without the need for the caller to be aware of the
- :meth:`.Dialect.get_dialect_cls` method.
-
-
- .. change::
- :tags: bug, ext
- :tickets: 3427
-
- Fixed regression in the :mod:`sqlalchemy.ext.mutable` extension
- as a result of the bugfix for :ticket:`3167`,
- where attribute and validation events are no longer
- called within the flush process. The mutable
- extension was relying upon this behavior in the case where a column
- level Python-side default were responsible for generating the new value
- on INSERT or UPDATE, or when a value were fetched from the RETURNING
- clause for "eager defaults" mode. The new value would not be subject
- to any event when populated and the mutable extension could not
- establish proper coercion or history listening. A new event
- :meth:`.InstanceEvents.refresh_flush` is added which the mutable
- extension now makes use of for this use case.
-
- .. change::
- :tags: feature, orm
- :tickets: 3427
-
- Added new event :meth:`.InstanceEvents.refresh_flush`, invoked
- when an INSERT or UPDATE level default value fetched via RETURNING
- or Python-side default is invoked within the flush process. This
- is to provide a hook that is no longer present as a result of
- :ticket:`3167`, where attribute and validation events are no longer
- called within the flush process.
-
- .. change::
- :tags: feature, ext
- :tickets: 3427
-
- Added a new semi-public method to :class:`.MutableBase`
- :meth:`.MutableBase._get_listen_keys`. Overriding this method
- is needed in the case where a :class:`.MutableBase` subclass needs
- events to propagate for attribute keys other than the key to which
- the mutable type is associated with, when intercepting the
- :meth:`.InstanceEvents.refresh` or
- :meth:`.InstanceEvents.refresh_flush` events. The current example of
- this is composites using :class:`.MutableComposite`.
-
- .. change::
- :tags: bug, engine
- :tickets: 3421
-
- Added support for the case of the misbehaving DBAPI that has
- pep-249 exception names linked to exception classes of an entirely
- different name, preventing SQLAlchemy's own exception wrapping from
- wrapping the error appropriately.
- The SQLAlchemy dialect in use needs to implement a new
- accessor :attr:`.DefaultDialect.dbapi_exception_translation_map`
- to support this feature; this is implemented now for the py-postgresql
- dialect.
-
- .. change::
- :tags: bug, orm
- :tickets: 3420
-
- The "lightweight named tuple" used when a :class:`_query.Query` returns
- rows failed to implement ``__slots__`` correctly such that it still
- had a ``__dict__``. This is resolved, but in the extremely
- unlikely case someone was assigning values to the returned tuples,
- that will no longer work.
-
- .. change::
- :tags: bug, engine
- :tickets: 3419
-
- Fixed bug involving the case when pool checkout event handlers are used
- and connection attempts are made in the handler itself which fail,
- the owning connection record would not be freed until the stack trace
- of the connect error itself were freed. For the case where a test
- pool of only a single connection were used, this means the pool would
- be fully checked out until that stack trace were freed. This mostly
- impacts very specific debugging scenarios and is unlikely to have been
- noticeable in any production application. The fix applies an
- explicit checkin of the record before re-raising the caught exception.
-
-
-.. changelog::
- :version: 1.0.4
- :released: May 7, 2015
-
- .. change::
- :tags: bug, orm
- :tickets: 3416
-
- Fixed unexpected-use regression where in the odd case that the
- primaryjoin of a relationship involved comparison to an unhashable
- type such as an HSTORE, lazy loads would fail due to a hash-oriented
- check on the statement parameters, modified in 1.0 as a result of
- :ticket:`3061` to use hashing and modified in :ticket:`3368`
- to occur in cases more common than "load on pending".
- The values are now checked for the ``__hash__`` attribute beforehand.
-
- .. change::
- :tags: bug, orm
- :tickets: 3412, 3347
-
- Liberalized an assertion that was added as part of :ticket:`3347`
- to protect against unknown conditions when splicing inner joins
- together within joined eager loads with ``innerjoin=True``; if
- some of the joins use a "secondary" table, the assertion needs to
- unwrap further joins in order to pass.
-
- .. change::
- :tags: bug, schema
- :tickets: 3411
-
- Fixed bug in enhanced constraint-attachment logic introduced in
- :ticket:`3341` where in the unusual case of a constraint that refers
- to a mixture of :class:`_schema.Column` objects and string column names
- at the same time, the auto-attach-on-column-attach logic will be
- skipped; for the constraint to be auto-attached in this case,
- all columns must be assembled on the target table up front.
- Added a new section to the migration document regarding the
- original feature as well as this change.
-
- .. seealso::
-
- :ref:`change_3341`
-
- .. change::
- :tags: bug, orm
- :tickets: 3409, 3320
-
- Repaired / added to tests yet more expressions that were reported
- as failing with the new 'entity' key value added to
- :attr:`_query.Query.column_descriptions`, the logic to discover the "from"
- clause is again reworked to accommodate columns from aliased classes,
- as well as to report the correct value for the "aliased" flag in these
- cases.
-
-
-.. changelog::
- :version: 1.0.3
- :released: April 30, 2015
-
- .. change::
- :tags: bug, orm, pypy
- :tickets: 3405
-
- Fixed regression from 0.9.10 prior to release due to :ticket:`3349`
- where the check for query state on :meth:`_query.Query.update` or
- :meth:`_query.Query.delete` compared the empty tuple to itself using ``is``,
- which fails on PyPy to produce ``True`` in this case; this would
- erroneously emit a warning in 0.9 and raise an exception in 1.0.
-
- .. change::
- :tags: feature, engine
- :tickets: 3379
-
- New features added to support engine/pool plugins with advanced
- functionality. Added a new "soft invalidate" feature to the
- connection pool at the level of the checked out connection wrapper
- as well as the :class:`._ConnectionRecord`. This works similarly
- to a modern pool invalidation in that connections aren't actively
- closed, but are recycled only on next checkout; this is essentially
- a per-connection version of that feature. A new event
- :meth:`_events.PoolEvents.soft_invalidate` is added to complement it.
-
- Also added new flag
- :attr:`.ExceptionContext.invalidate_pool_on_disconnect`.
- Allows an error handler within :meth:`_events.ConnectionEvents.handle_error`
- to maintain a "disconnect" condition, but to handle calling invalidate
- on individual connections in a specific manner within the event.
-
- .. change::
- :tags: feature, engine
- :tickets: 3355
-
- Added new event :class:`.DialectEvents.do_connect`, which allows
- interception / replacement of when the :meth:`.Dialect.connect`
- hook is called to create a DBAPI connection. Also added
- dialect plugin hooks :meth:`.Dialect.get_dialect_cls` and
- :meth:`.Dialect.engine_created` which allow external plugins to
- add events to existing dialects using entry points.
-
- .. change::
- :tags: bug, orm
- :tickets: 3403, 3320
-
- Fixed regression from 0.9.10 prior to release where the new addition
- of ``entity`` to the :attr:`_query.Query.column_descriptions` accessor
- would fail if the target entity was produced from a core selectable
- such as a :class:`_schema.Table` or :class:`_expression.CTE` object.
-
- .. change::
- :tags: feature, sql
-
- Added a placeholder method :meth:`.TypeEngine.compare_against_backend`
- which is now consumed by Alembic migrations as of 0.7.6. User-defined
- types can implement this method to assist in the comparison of
- a type against one reflected from the database.
-
- .. change::
- :tags: bug, orm
- :tickets: 3402
-
- Fixed regression within the flush process when an attribute were
- set to a SQL expression for an UPDATE, and the SQL expression when
- compared to the previous value of the attribute would produce a SQL
- comparison other than ``==`` or ``!=``, the exception "Boolean value
- of this clause is not defined" would raise. The fix ensures that
- the unit of work will not interpret the SQL expression in this way.
-
- .. change::
- :tags: bug, ext
- :tickets: 3397
-
- Fixed bug in association proxy where an any()/has()
- on an relationship->scalar non-object attribute comparison would fail,
- e.g.
- ``filter(Parent.some_collection_to_attribute.any(Child.attr == 'foo'))``
-
- .. change::
- :tags: bug, sql
- :tickets: 3396
-
- Fixed bug where the truncation of long labels in SQL could produce
- a label that overlapped another label that is not truncated; this
- because the length threshold for truncation was greater than
- the portion of the label that remains after truncation. These
- two values have now been made the same; label_length - 6.
- The effect here is that shorter column labels will be "truncated"
- where they would not have been truncated before.
-
- .. change::
- :tags: bug, orm
- :tickets: 3392
-
- Fixed unexpected use regression due to :ticket:`2992` where
- textual elements placed
- into the :meth:`_query.Query.order_by` clause in conjunction with joined
- eager loading would be added to the columns clause of the inner query
- in such a way that they were assumed to be table-bound column names,
- in the case where the joined eager load needs to wrap the query
- in a subquery to accommodate for a limit/offset.
-
- Originally, the behavior here was intentional, in that a query such
- as ``query(User).order_by('name').limit(1)``
- would order by ``user.name`` even if the query was modified by
- joined eager loading to be within a subquery, as ``'name'`` would
- be interpreted as a symbol to be located within the FROM clauses,
- in this case ``User.name``, which would then be copied into the
- columns clause to ensure it were present for ORDER BY. However, the
- feature fails to anticipate the case where ``order_by("name")`` refers
- to a specific label name present in the local columns clause already
- and not a name bound to a selectable in the FROM clause.
-
- Beyond that, the feature also fails for deprecated cases such as
- ``order_by("name desc")``, which, while it emits a
- warning that :func:`_expression.text` should be used here (note that the issue
- does not impact cases where :func:`_expression.text` is used explicitly),
- still produces a different query than previously where the "name desc"
- expression is copied into the columns clause inappropriately. The
- resolution is such that the "joined eager loading" aspect of the
- feature will skip over these so-called "label reference" expressions
- when augmenting the inner columns clause, as though they were
- :func:`_expression.text` constructs already.
-
- .. change::
- :tags: bug, sql
- :tickets: 3391
-
- Fixed regression due to :ticket:`3282` where the ``tables`` collection
- passed as a keyword argument to the :meth:`.DDLEvents.before_create`,
- :meth:`.DDLEvents.after_create`, :meth:`.DDLEvents.before_drop`, and
- :meth:`.DDLEvents.after_drop` events would no longer be a list
- of tables, but instead a list of tuples which contained a second
- entry with foreign keys to be added or dropped. As the ``tables``
- collection, while documented as not necessarily stable, has come
- to be relied upon, this change is considered a regression.
- Additionally, in some cases for "drop", this collection would
- be an iterator that would cause the operation to fail if
- prematurely iterated. The collection is now a list of table
- objects in all cases and test coverage for the format of this
- collection is now added.
-
-
- .. change::
- :tags: bug, orm
- :tickets: 3388
-
- Fixed a regression regarding the :meth:`.MapperEvents.instrument_class`
- event where its invocation was moved to be after the class manager's
- instrumentation of the class, which is the opposite of what the
- documentation for the event explicitly states. The rationale for the
- switch was due to Declarative taking the step of setting up
- the full "instrumentation manager" for a class before it was mapped
- for the purpose of the new ``@declared_attr`` features
- described in :ref:`feature_3150`, but the change was also made
- against the classical use of :class:`_orm.Mapper` for consistency.
- However, SQLSoup relies upon the instrumentation event happening
- before any instrumentation under classical mapping.
- The behavior is reverted in the case of classical and declarative
- mapping, the latter implemented by using a simple memoization
- without using class manager.
-
- .. change::
- :tags: bug, orm
- :tickets: 3387
-
- Fixed issue in new :meth:`.QueryEvents.before_compile` event where
- changes made to the :class:`_query.Query` object's collection of entities
- to load within the event would render in the SQL, but would not
- be reflected during the loading process.
-
-.. changelog::
- :version: 1.0.2
- :released: April 24, 2015
-
- .. change::
- :tags: bug, sql
- :tickets: 3338, 3385
-
- Fixed a regression that was incorrectly fixed in 1.0.0b4
- (hence becoming two regressions); reports that
- SELECT statements would GROUP BY a label name and fail was misconstrued
- that certain backends such as SQL Server should not be emitting
- ORDER BY or GROUP BY on a simple label name at all; when in fact,
- we had forgotten that 0.9 was already emitting ORDER BY on a simple
- label name for all backends, as described in :ref:`migration_1068`,
- even though 1.0 includes a rewrite of this logic as part of
- :ticket:`2992`. As far
- as emitting GROUP BY against a simple label, even PostgreSQL has
- cases where it will raise an error even though the label to group
- on should be apparent, so it is clear that GROUP BY should never
- be rendered in this way automatically.
-
- In 1.0.2, SQL Server, Firebird and others will again emit ORDER BY on
- a simple label name when passed a
- :class:`.Label` construct that is also present in the columns clause.
- Additionally, no backend will emit GROUP BY against the simple label
- name only when passed a :class:`.Label` construct.
-
- .. change::
- :tags: bug, orm, declarative
- :tickets: 3383
-
- Fixed unexpected use regression regarding the declarative
- ``__declare_first__`` and ``__declare_last__`` accessors where these
- would no longer be called on the superclass of the declarative base.
-
-.. changelog::
- :version: 1.0.1
- :released: April 23, 2015
-
- .. change::
- :tags: bug, firebird
- :tickets: 3380
-
- Fixed a regression due to :ticket:`3034` where limit/offset
- clauses were not properly interpreted by the Firebird dialect.
- Pull request courtesy effem-git.
-
- .. change::
- :tags: bug, firebird
- :tickets: 3381
-
- Fixed support for "literal_binds" mode when using limit/offset
- with Firebird, so that the values are again rendered inline when
- this is selected. Related to :ticket:`3034`.
-
- .. change::
- :tags: bug, sqlite
- :tickets: 3378
-
- Fixed a regression due to :ticket:`3282`, where due to the fact that
- we attempt to assume the availability of ALTER when creating/dropping
- schemas, in the case of SQLite we simply said to not worry about
- foreign keys at all, since ALTER is not available, when creating
- and dropping tables. This meant that the sorting of tables was
- basically skipped in the case of SQLite, and for the vast majority
- of SQLite use cases, this is not an issue.
-
- However, users who were doing DROPs on SQLite
- with tables that contained data and with referential integrity
- turned on would then experience errors, as the
- dependency sorting *does* matter in the case of DROP with
- enforced constraints, when those tables have data (SQLite will still
- happily let you create foreign keys to nonexistent tables and drop
- tables referring to existing ones with constraints enabled, as long as
- there's no data being referenced).
-
- In order to maintain the new feature of :ticket:`3282` while still
- allowing a SQLite DROP operation to maintain ordering, we now
- do the sort with full FKs taken under consideration, and if we encounter
- an unresolvable cycle, only *then* do we forego attempting to sort
- the tables; we instead emit a warning and go with the unsorted list.
- If an environment needs both ordered DROPs *and* has foreign key
- cycles, then the warning notes they will need to restore the
- ``use_alter`` flag to their :class:`_schema.ForeignKey` and
- :class:`_schema.ForeignKeyConstraint` objects so that just those objects will
- be omitted from the dependency sort.
-
- .. seealso::
-
- :ref:`feature_3282` - contains an updated note about SQLite.
-
- .. change::
- :tags: bug, sql
- :tickets: 3372
-
- Fixed issue where a straight SELECT EXISTS query would fail to
- assign the proper result type of Boolean to the result mapping, and
- instead would leak column types from within the query into the
- result map. This issue exists in 0.9 and earlier as well, however
- has less of an impact in those versions. In 1.0, due to :ticket:`918`
- this becomes a regression in that we now rely upon the result mapping
- to be very accurate, else we can assign result-type processors to
- the wrong column. In all versions, this issue also has the effect
- that a simple EXISTS will not apply the Boolean type handler, leading
- to simple 1/0 values for backends without native boolean instead of
- True/False. The fix includes that an EXISTS columns argument
- will be anon-labeled like other column expressions; a similar fix is
- implemented for pure-boolean expressions like ``not_(True())``.
-
- .. change::
- :tags: bug, orm
- :tickets: 3374
-
- Fixed issue where a query of the form
- ``query(B).filter(B.a != A(id=7))`` would render the ``NEVER_SET``
- symbol, when
- given a transient object. For a persistent object, it would
- always use the persisted database value and not the currently
- set value. Assuming autoflush is turned on, this usually would
- not be apparent for persistent values, as any pending changes
- would be flushed first in any case. However, this is inconsistent
- vs. the logic used for the non-negated comparison,
- ``query(B).filter(B.a == A(id=7))``, which does use the
- current value and additionally allows comparisons to transient
- objects. The comparison now uses the current value and not
- the database-persisted value.
-
- Unlike the other ``NEVER_SET`` issues that are repaired as regressions
- caused by :ticket:`3061` in this release, this particular issue is
- present at least as far back as 0.8 and possibly earlier, however it
- was discovered as a result of repairing the related ``NEVER_SET``
- issues.
-
- .. seealso::
-
- :ref:`bug_3374`
-
- .. change::
- :tags: bug, orm
- :tickets: 3371
-
- Fixed unexpected use regression cause by :ticket:`3061` where
- the NEVER_SET
- symbol could leak into relationship-oriented queries, including
- ``filter()`` and ``with_parent()`` queries. The ``None`` symbol
- is returned in all cases, however many of these queries have never
- been correctly supported in any case, and produce comparisons
- to NULL without using the IS operator. For this reason, a warning
- is also added to that subset of relationship queries that don't
- currently provide for ``IS NULL``.
-
- .. seealso::
-
- :ref:`bug_3371`
-
-
- .. change::
- :tags: bug, orm
- :tickets: 3368
-
- Fixed a regression caused by :ticket:`3061` where the
- NEVER_SET symbol could leak into a lazyload query, subsequent
- to the flush of a pending object. This would occur typically
- for a many-to-one relationship that does not use a simple
- "get" strategy. The good news is that the fix improves efficiency
- vs. 0.9, because we can now skip the SELECT statement entirely
- when we detect NEVER_SET symbols present in the parameters; prior to
- :ticket:`3061`, we couldn't discern if the None here were set or not.
-
-
-.. changelog::
- :version: 1.0.0
- :released: April 16, 2015
-
- .. change::
- :tags: bug, orm
- :tickets: 3367
-
- Identified an inconsistency when handling :meth:`_query.Query.join` to the
- same target more than once; it implicitly dedupes only in the case of
- a relationship join, and due to :ticket:`3233`, in 1.0 a join
- to the same table twice behaves differently than 0.9 in that it no
- longer erroneously aliases. To help document this change,
- the verbiage regarding :ticket:`3233` in the migration notes has
- been generalized, and a warning has been added when :meth:`_query.Query.join`
- is called against the same target relationship more than once.
-
- .. change::
- :tags: bug, orm
- :tickets: 3364
-
- Made a small improvement to the heuristics of relationship when
- determining remote side with semi-self-referential (e.g. two joined
- inh subclasses referring to each other), non-simple join conditions
- such that the parententity is taken into account and can reduce the
- need for using the ``remote()`` annotation; this can restore some
- cases that might have worked without the annotation prior to 0.9.4
- via :ticket:`2948`.
-
- .. change::
- :tags: bug, mssql
- :tickets: 3360
-
- Fixed a regression where the "last inserted id" mechanics would
- fail to store the correct value for MSSQL on an INSERT where the
- primary key value was present in the insert params before execution,
- as well as in the case where an INSERT from SELECT would state the
- target columns as column objects, instead of string keys.
-
-
- .. change::
- :tags: bug, mssql
-
- Using the ``Binary`` constructor now present in pymssql rather than
- patching one in. Pull request courtesy Ramiro Morales.
-
- .. change::
- :tags: bug, tests
- :tickets: 3356
-
- Fixed the pathing used when tests run; for sqla_nose.py and py.test,
- the "./lib" prefix is again inserted at the head of sys.path but
- only if sys.flags.no_user_site isn't set; this makes it act just
- like the way Python puts "." in the current path by default.
- For tox, we are setting the PYTHONNOUSERSITE flag now.
-
- .. change::
- :tags: feature, sql
- :tickets: 3084
-
- The topological sorting used to sort :class:`_schema.Table` objects
- and available via the :attr:`_schema.MetaData.sorted_tables` collection
- will now produce a **deterministic** ordering; that is, the same
- ordering each time given a set of tables with particular names
- and dependencies. This is to help with comparison of DDL scripts
- and other use cases. The tables are sent to the topological sort
- sorted by name, and the topological sort itself will process
- the incoming data in an ordered fashion. Pull request
- courtesy Sebastian Bank.
-
- .. seealso::
-
- :ref:`feature_3084`
-
- .. change::
- :tags: feature, orm
-
- Added new argument :paramref:`.Query.update.update_args` which allows
- kw arguments such as ``mysql_limit`` to be passed to the underlying
- :class:`_expression.Update` construct. Pull request courtesy Amir Sadoughi.
-
-.. changelog::
- :version: 1.0.0b5
- :released: April 3, 2015
-
- .. change::
- :tags: bug, orm
- :tickets: 3349
-
- :class:`_query.Query` doesn't support joins, subselects, or special
- FROM clauses when using the :meth:`_query.Query.update` or
- :meth:`_query.Query.delete` methods; instead of silently ignoring these
- fields if methods like :meth:`_query.Query.join` or
- :meth:`_query.Query.select_from` has been called, an error is raised.
- In 0.9.10 this only emits a warning.
-
- .. change::
- :tags: bug, orm
-
- Added a list() call around a weak dictionary used within the
- commit phase of the session, which without it could cause
- a "dictionary changed size during iter" error if garbage collection
- interacted within the process. Change was introduced by
- #3139.
-
- .. change::
- :tags: bug, postgresql
- :tickets: 3343
-
- Fixed bug where updated PG index reflection as a result of
- :ticket:`3184` would cause index operations to fail on PostgreSQL
- versions 8.4 and earlier. The enhancements are now
- disabled when using an older version of PostgreSQL.
-
- .. change::
- :tags: bug, sql
- :tickets: 3346
-
- The warning emitted by the unicode type for a non-unicode type
- has been liberalized to warn for values that aren't even string
- values, such as integers; previously, the updated warning system
- of 1.0 made use of string formatting operations which
- would raise an internal TypeError. While these cases should ideally
- raise totally, some backends like SQLite and MySQL do accept them
- and are potentially in use by legacy code, not to mention that they
- will always pass through if unicode conversion is turned off
- for the target backend.
-
- .. change::
- :tags: bug, orm
- :tickets: 3347
-
- Fixed a bug related to "nested" inner join eager loading, which
- exists in 0.9 as well but is more of a regression in 1.0 due to
- :ticket:`3008` which turns on "nested" by default, such that
- a joined eager load that travels across sibling paths from a common
- ancestor using innerjoin=True will correctly splice each "innerjoin"
- sibling into the appropriate part of the join, when a series of
- inner/outer joins are mixed together.
-
-.. changelog::
- :version: 1.0.0b4
- :released: March 29, 2015
-
- .. change::
- :tags: bug, mssql, oracle, firebird, sybase
- :tickets: 3338
-
- Turned off the "simple order by" flag on the MSSQL, Oracle dialects;
- this is the flag that per :ticket:`2992` causes an order by or group by
- an expression that's also in the columns clause to be copied by
- label, even if referenced as the expression object. The behavior
- for MSSQL is now the old behavior that copies the whole expression
- in by default, as MSSQL can be picky on these particularly in
- GROUP BY expressions. The flag is also turned off defensively
- for the Firebird and Sybase dialects.
-
- .. note:: this resolution was incorrect, please see version 1.0.2
- for a rework of this resolution.
-
- .. change::
- :tags: feature, schema
- :tickets: 3341
-
- The "auto-attach" feature of constraints such as :class:`.UniqueConstraint`
- and :class:`.CheckConstraint` has been further enhanced such that
- when the constraint is associated with non-table-bound :class:`_schema.Column`
- objects, the constraint will set up event listeners with the
- columns themselves such that the constraint auto attaches at the
- same time the columns are associated with the table. This in particular
- helps in some edge cases in declarative but is also of general use.
-
- .. seealso::
-
- :ref:`change_3341`
-
- .. change::
- :tags: bug, sql
- :tickets: 3340
-
- Fixed bug in new "label resolution" feature of :ticket:`2992` where
- a label that was anonymous, then labeled again with a name, would
- fail to be locatable via a textual label. This situation occurs
- naturally when a mapped :func:`.column_property` is given an
- explicit label in a query.
-
- .. change::
- :tags: bug, sql
- :tickets: 3335
-
- Fixed bug in new "label resolution" feature of :ticket:`2992` where
- the string label placed in the order_by() or group_by() of a statement
- would place higher priority on the name as found
- inside the FROM clause instead of a more locally available name
- inside the columns clause.
-
-.. changelog::
- :version: 1.0.0b3
- :released: March 20, 2015
-
- .. change::
- :tags: bug, mysql
- :tickets: 2771
-
- Repaired the commit for issue #2771 which was inadvertently commented
- out.
-
-
-.. changelog::
- :version: 1.0.0b2
- :released: March 20, 2015
-
- .. change::
- :tags: bug, mysql
- :tickets: 2771
-
- Fixes to fully support using the ``'utf8mb4'`` MySQL-specific charset
- with MySQL dialects, in particular MySQL-Python and PyMySQL. In
- addition, MySQL databases that report more unusual charsets such as
- 'koi8u' or 'eucjpms' will also work correctly. Pull request
- courtesy Thomas Grainger.
-
- .. change::
- :tags: change, orm, declarative
- :tickets: 3331
-
- Loosened some restrictions that were added to ``@declared_attr``
- objects, such that they were prevented from being called outside
- of the declarative process; this is related to the enhancements
- of #3150 which allow ``@declared_attr`` to return a value that is
- cached based on the current class as it's being configured.
- The exception raise has been removed, and the behavior changed
- so that outside of the declarative process, the function decorated by
- ``@declared_attr`` is called every time just like a regular
- ``@property``, without using any caching, as none is available
- at this stage.
-
- .. change::
- :tags: bug, engine
- :tickets: 3330, 3329
-
- The "auto close" for :class:`_engine.ResultProxy` is now a "soft" close.
- That is, after exhausting all rows using the fetch methods, the
- DBAPI cursor is released as before and the object may be safely
- discarded, but the fetch methods may continue to be called for which
- they will return an end-of-result object (None for fetchone, empty list
- for fetchmany and fetchall). Only if :meth:`_engine.ResultProxy.close`
- is called explicitly will these methods raise the "result is closed"
- error.
-
- .. seealso::
-
- :ref:`change_3330`
-
- .. change::
- :tags: bug, orm
- :tickets: 3327
-
- Fixed unexpected use regression from pullreq github:137 where
- Py2K unicode literals (e.g. ``u""``) would not be accepted by the
- :paramref:`_orm.relationship.cascade` option.
- Pull request courtesy Julien Castets.
-
-
-.. changelog::
- :version: 1.0.0b1
- :released: March 13, 2015
-
- Version 1.0.0b1 is the first release of the 1.0 series. Many changes
- described here are also present in the 0.9 and sometimes the 0.8
- series as well. For changes that are specific to 1.0 with an emphasis
- on compatibility concerns, see :doc:`/changelog/migration_10`.
-
- .. change::
- :tags: feature, ext
- :tickets: 3054
-
- Added a new extension suite :mod:`sqlalchemy.ext.baked`. This
- simple but unusual system allows for a dramatic savings in Python
- overhead for the construction and processing of orm :class:`_query.Query`
- objects, from query construction up through rendering of a string
- SQL statement.
-
- .. seealso::
-
- :ref:`baked_toplevel`
-
- .. change::
- :tags: bug, postgresql
- :tickets: 3319
-
- The PostgreSQL :class:`_postgresql.ENUM` type will emit a
- DROP TYPE instruction when a plain ``table.drop()`` is called,
- assuming the object is not associated directly with a
- :class:`_schema.MetaData` object. In order to accommodate the use case of
- an enumerated type shared between multiple tables, the type should
- be associated directly with the :class:`_schema.MetaData` object; in this
- case the type will only be created at the metadata level, or if
- created directly. The rules for create/drop of
- PostgreSQL enumerated types have been highly reworked in general.
-
- .. seealso::
-
- :ref:`change_3319`
-
- .. change::
- :tags: feature, orm
- :tickets: 3317
-
- Added a new event suite :class:`.QueryEvents`. The
- :meth:`.QueryEvents.before_compile` event allows the creation
- of functions which may place additional modifications to
- :class:`_query.Query` objects before the construction of the SELECT
- statement. It is hoped that this event be made much more
- useful via the advent of a new inspection system that will
- allow for detailed modifications to be made against
- :class:`_query.Query` objects in an automated fashion.
-
- .. seealso::
-
- :class:`.QueryEvents`
-
-
- .. change::
- :tags: feature, orm
- :tickets: 3249
-
- The subquery wrapping which occurs when joined eager loading
- is used with a one-to-many query that also features LIMIT,
- OFFSET, or DISTINCT has been disabled in the case of a one-to-one
- relationship, that is a one-to-many with
- :paramref:`_orm.relationship.uselist` set to False. This will produce
- more efficient queries in these cases.
-
- .. seealso::
-
- :ref:`change_3249`
-
-
- .. change::
- :tags: bug, orm
- :tickets: 3301
-
- Fixed bug where the session attachment error "object is already
- attached to session X" would fail to prevent the object from
- also being attached to the new session, in the case that execution
- continued after the error raise occurred.
-
- .. change::
- :tags: bug, orm, declarative
- :tickets: 3219, 3240
-
- Fixed bug where using an ``__abstract__`` mixin in the middle
- of a declarative inheritance hierarchy would prevent attributes
- and configuration being correctly propagated from the base class
- to the inheriting class.
-
- .. change::
- :tags: feature, sql
- :tickets: 918
-
- The SQL compiler now generates the mapping of expected columns
- such that they are matched to the received result set positionally,
- rather than by name. Originally, this was seen as a way to handle
- cases where we had columns returned with difficult-to-predict names,
- though in modern use that issue has been overcome by anonymous
- labeling. In this version, the approach basically reduces function
- call count per-result by a few dozen calls, or more for larger
- sets of result columns. The approach still degrades into a modern
- version of the old approach if any discrepancy in size exists between
- the compiled set of columns versus what was received, so there's no
- issue for partially or fully textual compilation scenarios where these
- lists might not line up.
-
- .. change::
- :tags: feature, postgresql
-
- The PG8000 dialect now supports the
- :paramref:`_sa.create_engine.encoding` parameter, by setting up
- the client encoding on the connection which is then intercepted
- by pg8000. Pull request courtesy Tony Locke.
-
- .. change::
- :tags: feature, postgresql
-
- Added support for PG8000's native JSONB feature. Pull request
- courtesy Tony Locke.
-
- .. change::
- :tags: change, orm
-
- Mapped attributes marked as deferred without explicit undeferral
- will now remain "deferred" even if their column is otherwise
- present in the result set in some way. This is a performance
- enhancement in that an ORM load no longer spends time searching
- for each deferred column when the result set is obtained. However,
- for an application that has been relying upon this, an explicit
- :func:`.undefer` or similar option should now be used.
-
- .. change::
- :tags: feature, orm
- :tickets: 3307
-
- Mapped state internals have been reworked to allow for a 50% reduction
- in callcounts specific to the "expiration" of objects, as in
- the "auto expire" feature of :meth:`.Session.commit` and
- for :meth:`.Session.expire_all`, as well as in the "cleanup" step
- which occurs when object states are garbage collected.
-
- .. change::
- :tags: bug, mysql
-
- The MySQL dialect now supports CAST on types that are constructed
- as :class:`.TypeDecorator` objects.
-
- .. change::
- :tags: bug, mysql
- :tickets: 3237
-
- A warning is emitted when :func:`.cast` is used with the MySQL
- dialect on a type where MySQL does not support CAST; MySQL only
- supports CAST on a subset of datatypes. SQLAlchemy has for a long
- time just omitted the CAST for unsupported types in the case of
- MySQL. While we don't want to change this now, we emit a warning
- to show that it's taken place. A warning is also emitted when
- a CAST is used with an older MySQL version (< 4) that doesn't support
- CAST at all, it's skipped in this case as well.
-
- .. change::
- :tags: feature, sql
- :tickets: 3087
-
- Literal values within a :class:`.DefaultClause`, which is invoked
- when using the :paramref:`_schema.Column.server_default` parameter, will
- now be rendered using the "inline" compiler, so that they are rendered
- as-is, rather than as bound parameters.
-
- .. seealso::
-
- :ref:`change_3087`
-
- .. change::
- :tags: feature, oracle
-
- Added support for cx_oracle connections to a specific service
- name, as opposed to a tns name, by passing ``?service_name=<name>``
- to the URL. Pull request courtesy Sławomir Ehlert.
-
- .. change::
- :tags: feature, mysql
- :tickets: 3155
-
- The MySQL dialect now renders TIMESTAMP with NULL / NOT NULL in
- all cases, so that MySQL 5.6.6 with the
- ``explicit_defaults_for_timestamp`` flag enabled will
- will allow TIMESTAMP to continue to work as expected when
- ``nullable=False``. Existing applications are unaffected as
- SQLAlchemy has always emitted NULL for a TIMESTAMP column that
- is ``nullable=True``.
-
- .. seealso::
-
- :ref:`change_3155`
-
- :ref:`mysql_timestamp_null`
-
- .. change::
- :tags: bug, schema
- :tickets: 3299, 3067
-
- The :class:`.CheckConstraint` construct now supports naming
- conventions that include the token ``%(column_0_name)s``; the
- constraint expression is scanned for columns. Additionally,
- naming conventions for check constraints that don't include the
- ``%(constraint_name)s`` token will now work for :class:`.SchemaType`-
- generated constraints, such as those of :class:`.Boolean` and
- :class:`.Enum`; this stopped working in 0.9.7 due to :ticket:`3067`.
-
- .. seealso::
-
- :ref:`naming_check_constraints`
-
- :ref:`naming_schematypes`
-
-
- .. change::
- :tags: feature, postgresql, pypy
- :tickets: 3052
-
- Added support for the psycopg2cffi DBAPI on pypy. Pull request
- courtesy shauns.
-
- .. seealso::
-
- :mod:`sqlalchemy.dialects.postgresql.psycopg2cffi`
-
- .. change::
- :tags: feature, orm
- :tickets: 3262
-
- A warning is emitted when the same polymorphic identity is assigned
- to two different mappers in the same hierarchy. This is typically a
- user error and means that the two different mapping types cannot be
- correctly distinguished at load time. Pull request courtesy
- Sebastian Bank.
-
- .. change::
- :tags: feature, sql
-
- The type of expression is reported when an object passed to a
- SQL expression unit can't be interpreted as a SQL fragment;
- pull request courtesy Ryan P. Kelly.
-
- .. change::
- :tags: bug, orm
- :tickets: 3227, 3242, 1326
-
- The primary :class:`_orm.Mapper` of a :class:`_query.Query` is now passed to the
- :meth:`.Session.get_bind` method when calling upon
- :meth:`_query.Query.count`, :meth:`_query.Query.update`, :meth:`_query.Query.delete`,
- as well as queries against mapped columns,
- :obj:`.column_property` objects, and SQL functions and expressions
- derived from mapped columns. This allows sessions that rely upon
- either customized :meth:`.Session.get_bind` schemes or "bound" metadata
- to work in all relevant cases.
-
- .. seealso::
-
- :ref:`bug_3227`
-
- .. change::
- :tags: enhancement, sql
- :tickets: 3074
-
- Custom dialects that implement :class:`.GenericTypeCompiler` can
- now be constructed such that the visit methods receive an indication
- of the owning expression object, if any. Any visit method that
- accepts keyword arguments (e.g. ``**kw``) will in most cases
- receive a keyword argument ``type_expression``, referring to the
- expression object that the type is contained within. For columns
- in DDL, the dialect's compiler class may need to alter its
- ``get_column_specification()`` method to support this as well.
- The ``UserDefinedType.get_col_spec()`` method will also receive
- ``type_expression`` if it provides ``**kw`` in its argument
- signature.
-
- .. change::
- :tags: bug, sql
- :tickets: 3288
-
- The multi-values version of :meth:`_expression.Insert.values` has been
- repaired to work more usefully with tables that have Python-
- side default values and/or functions, as well as server-side
- defaults. The feature will now work with a dialect that uses
- "positional" parameters; a Python callable will also be
- invoked individually for each row just as is the case with an
- "executemany" style invocation; a server- side default column
- will no longer implicitly receive the value explicitly
- specified for the first row, instead refusing to invoke
- without an explicit value.
-
- .. seealso::
-
- :ref:`bug_3288`
-
- .. change::
- :tags: feature, general
-
- Structural memory use has been improved via much more significant use
- of ``__slots__`` for many internal objects. This optimization is
- particularly geared towards the base memory size of large applications
- that have lots of tables and columns, and greatly reduces memory
- size for a variety of high-volume objects including event listening
- internals, comparator objects and parts of the ORM attribute and
- loader strategy system.
-
- .. seealso::
-
- :ref:`feature_slots`
-
- .. change::
- :tags: bug, mysql
- :tickets: 3283
-
- The :class:`.mysql.SET` type has been overhauled to no longer
- assume that the empty string, or a set with a single empty string
- value, is in fact a set with a single empty string; instead, this
- is by default treated as the empty set. In order to handle persistence
- of a :class:`.mysql.SET` that actually wants to include the blank
- value ``''`` as a legitimate value, a new bitwise operational mode
- is added which is enabled by the
- :paramref:`.mysql.SET.retrieve_as_bitwise` flag, which will persist
- and retrieve values unambiguously using their bitflag positioning.
- Storage and retrieval of unicode values for driver configurations
- that aren't converting unicode natively is also repaired.
-
- .. seealso::
-
- :ref:`change_3283`
-
-
- .. change::
- :tags: feature, schema
- :tickets: 3282
-
- The DDL generation system of :meth:`_schema.MetaData.create_all`
- and :meth:`_schema.MetaData.drop_all` has been enhanced to in most
- cases automatically handle the case of mutually dependent
- foreign key constraints; the need for the
- :paramref:`_schema.ForeignKeyConstraint.use_alter` flag is greatly
- reduced. The system also works for constraints which aren't given
- a name up front; only in the case of DROP is a name required for
- at least one of the constraints involved in the cycle.
-
- .. seealso::
-
- :ref:`feature_3282`
-
- .. change::
- :tags: feature, schema
-
- Added a new accessor :attr:`_schema.Table.foreign_key_constraints`
- to complement the :attr:`_schema.Table.foreign_keys` collection,
- as well as :attr:`_schema.ForeignKeyConstraint.referred_table`.
-
- .. change::
- :tags: bug, sqlite
- :tickets: 3244, 3261
-
- UNIQUE and FOREIGN KEY constraints are now fully reflected on
- SQLite both with and without names. Previously, foreign key
- names were ignored and unnamed unique constraints were skipped.
- Thanks to Jon Nelson for assistance with this.
-
- .. change::
- :tags: feature, examples
-
- A new suite of examples dedicated to providing a detailed study
- into performance of SQLAlchemy ORM and Core, as well as the DBAPI,
- from multiple perspectives. The suite runs within a container
- that provides built in profiling displays both through console
- output as well as graphically via the RunSnake tool.
-
- .. seealso::
-
- :ref:`examples_performance`
-
- .. change::
- :tags: feature, orm
- :tickets: 3100
-
- A new series of :class:`.Session` methods which provide hooks
- directly into the unit of work's facility for emitting INSERT
- and UPDATE statements has been created. When used correctly,
- this expert-oriented system can allow ORM-mappings to be used
- to generate bulk insert and update statements batched into
- executemany groups, allowing the statements to proceed at
- speeds that rival direct use of the Core.
-
- .. seealso::
-
- :ref:`bulk_operations`
-
- .. change::
- :tags: feature, mssql
- :tickets: 3039
-
- SQL Server 2012 now recommends VARCHAR(max), NVARCHAR(max),
- VARBINARY(max) for large text/binary types. The MSSQL dialect will
- now respect this based on version detection, as well as the new
- ``deprecate_large_types`` flag.
-
- .. seealso::
-
- :ref:`mssql_large_type_deprecation`
-
- .. change::
- :tags: bug, sqlite
- :tickets: 3257
-
- The SQLite dialect, when using the :class:`_sqlite.DATE`,
- :class:`_sqlite.TIME`,
- or :class:`_sqlite.DATETIME` types, and given a ``storage_format`` that
- only renders numbers, will render the types in DDL as
- ``DATE_CHAR``, ``TIME_CHAR``, and ``DATETIME_CHAR``, so that despite the
- lack of alpha characters in the values, the column will still
- deliver the "text affinity". Normally this is not needed, as the
- textual values within the default storage formats already
- imply text.
-
- .. seealso::
-
- :ref:`sqlite_datetime`
-
- .. change::
- :tags: bug, engine
- :tickets: 3266
-
- The engine-level error handling and wrapping routines will now
- take effect in all engine connection use cases, including
- when user-custom connect routines are used via the
- :paramref:`_sa.create_engine.creator` parameter, as well as when
- the :class:`_engine.Connection` encounters a connection error on
- revalidation.
-
- .. seealso::
-
- :ref:`change_3266`
-
- .. change::
- :tags: feature, oracle
-
- New Oracle DDL features for tables, indexes: COMPRESS, BITMAP.
- Patch courtesy Gabor Gombas.
-
- .. change::
- :tags: bug, oracle
-
- An alias name will be properly quoted when referred to using the
- ``%(name)s`` token inside the :meth:`_expression.Select.with_hint` method.
- Previously, the Oracle backend hadn't implemented this quoting.
-
- .. change::
- :tags: feature, oracle
- :tickets: 3220
-
- Added support for CTEs under Oracle. This includes some tweaks
- to the aliasing syntax, as well as a new CTE feature
- :meth:`_expression.CTE.suffix_with`, which is useful for adding in special
- Oracle-specific directives to the CTE.
-
- .. seealso::
-
- :ref:`change_3220`
-
- .. change::
- :tags: feature, mysql
- :tickets: 3121
-
- Updated the "supports_unicode_statements" flag to True for MySQLdb
- and Pymysql under Python 2. This refers to the SQL statements
- themselves, not the parameters, and affects issues such as table
- and column names using non-ASCII characters. These drivers both
- appear to support Python 2 Unicode objects without issue in modern
- versions.
-
- .. change::
- :tags: bug, mysql
- :tickets: 3263
-
- The :meth:`.ColumnOperators.match` operator is now handled such that the
- return type is not strictly assumed to be boolean; it now
- returns a :class:`.Boolean` subclass called :class:`.MatchType`.
- The type will still produce boolean behavior when used in Python
- expressions, however the dialect can override its behavior at
- result time. In the case of MySQL, while the MATCH operator
- is typically used in a boolean context within an expression,
- if one actually queries for the value of a match expression, a
- floating point value is returned; this value is not compatible
- with SQLAlchemy's C-based boolean processor, so MySQL's result-set
- behavior now follows that of the :class:`.Float` type.
- A new operator object ``notmatch_op`` is also added to better allow
- dialects to define the negation of a match operation.
-
- .. seealso::
-
- :ref:`change_3263`
-
- .. change::
- :tags: bug, postgresql
- :tickets: 3264
-
- The :meth:`.PGDialect.has_table` method will now query against
- ``pg_catalog.pg_table_is_visible(c.oid)``, rather than testing
- for an exact schema match, when the schema name is None; this
- so that the method will also illustrate that temporary tables
- are present. Note that this is a behavioral change, as PostgreSQL
- allows a non-temporary table to silently overwrite an existing
- temporary table of the same name, so this changes the behavior
- of ``checkfirst`` in that unusual scenario.
-
- .. seealso::
-
- :ref:`change_3264`
-
- .. change::
- :tags: bug, sql
- :tickets: 3260
-
- Fixed bug in :meth:`_schema.Table.tometadata` method where the
- :class:`.CheckConstraint` associated with a :class:`.Boolean`
- or :class:`.Enum` type object would be doubled in the target table.
- The copy process now tracks the production of this constraint object
- as local to a type object.
-
- .. change::
- :tags: feature, orm
- :tickets: 3217
-
- Added a parameter :paramref:`.Query.join.isouter` which is synonymous
- with calling :meth:`_query.Query.outerjoin`; this flag is to provide a more
- consistent interface compared to Core :meth:`_expression.FromClause.join`.
- Pull request courtesy Jonathan Vanasco.
-
- .. change::
- :tags: bug, sql
- :tickets: 3243
-
- The behavioral contract of the :attr:`_schema.ForeignKeyConstraint.columns`
- collection has been made consistent; this attribute is now a
- :class:`_expression.ColumnCollection` like that of all other constraints and
- is initialized at the point when the constraint is associated with
- a :class:`_schema.Table`.
-
- .. seealso::
-
- :ref:`change_3243`
-
- .. change::
- :tags: bug, orm
- :tickets: 3256
-
- The :meth:`.PropComparator.of_type` modifier has been
- improved in conjunction with loader directives such as
- :func:`_orm.joinedload` and :func:`.contains_eager` such that if
- two :meth:`.PropComparator.of_type` modifiers of the same
- base type/path are encountered, they will be joined together
- into a single "polymorphic" entity, rather than replacing
- the entity of type A with the one of type B. E.g.
- a joinedload of ``A.b.of_type(BSub1)->BSub1.c`` combined with
- joinedload of ``A.b.of_type(BSub2)->BSub2.c`` will create a
- single joinedload of ``A.b.of_type((BSub1, BSub2)) -> BSub1.c, BSub2.c``,
- without the need for the ``with_polymorphic`` to be explicit
- in the query.
-
- .. seealso::
-
- :ref:`eagerloading_polymorphic_subtypes` - contains an updated
- example illustrating the new format.
-
- .. change::
- :tags: bug, sql
- :tickets: 3245
-
- The :attr:`_schema.Column.key` attribute is now used as the source of
- anonymous bound parameter names within expressions, to match the
- existing use of this value as the key when rendered in an INSERT
- or UPDATE statement. This allows :attr:`_schema.Column.key` to be used
- as a "substitute" string to work around a difficult column name
- that doesn't translate well into a bound parameter name. Note that
- the paramstyle is configurable on :func:`_sa.create_engine` in any case,
- and most DBAPIs today support a named and positional style.
-
- .. change::
- :tags: bug, sql
-
- Fixed the name of the :paramref:`.PoolEvents.reset.dbapi_connection`
- parameter as passed to this event; in particular this affects
- usage of the "named" argument style for this event. Pull request
- courtesy Jason Goldberger.
-
- .. change::
- :tags: feature, sql
-
- Added a new parameter :paramref:`.Table.tometadata.name` to
- the :meth:`_schema.Table.tometadata` method. Similar to
- :paramref:`.Table.tometadata.schema`, this argument causes the newly
- copied :class:`_schema.Table` to take on the new name instead of
- the existing one. An interesting capability this adds is that of
- copying a :class:`_schema.Table` object to the *same* :class:`_schema.MetaData`
- target with a new name. Pull request courtesy n.d. parker.
-
- .. change::
- :tags: bug, orm
-
- Repaired support of the ``copy.deepcopy()`` call when used by the
- :class:`.orm.util.CascadeOptions` argument, which occurs
- if ``copy.deepcopy()`` is being used with :func:`_orm.relationship`
- (not an officially supported use case). Pull request courtesy
- duesenfranz.
-
- .. change::
- :tags: bug, sql
- :tickets: 3170
-
- Reversing a change that was made in 0.9, the "singleton" nature
- of the "constants" :func:`.null`, :func:`.true`, and :func:`.false`
- has been reverted. These functions returning a "singleton" object
- had the effect that different instances would be treated as the
- same regardless of lexical use, which in particular would impact
- the rendering of the columns clause of a SELECT statement.
-
- .. seealso::
-
- :ref:`bug_3170`
-
- .. change::
- :tags: bug, orm
- :tickets: 3139
-
- Fixed bug where :meth:`.Session.expunge` would not fully detach
- the given object if the object had been subject to a delete
- operation that was flushed, but not committed. This would also
- affect related operations like :func:`.make_transient`.
-
- .. seealso::
-
- :ref:`bug_3139`
-
- .. change::
- :tags: bug, orm
- :tickets: 3230
-
- A warning is emitted in the case of multiple relationships that
- ultimately will populate a foreign key column in conflict with
- another, where the relationships are attempting to copy values
- from different source columns. This occurs in the case where
- composite foreign keys with overlapping columns are mapped to
- relationships that each refer to a different referenced column.
- A new documentation section illustrates the example as well as how
- to overcome the issue by specifying "foreign" columns specifically
- on a per-relationship basis.
-
- .. seealso::
-
- :ref:`relationship_overlapping_foreignkeys`
-
- .. change::
- :tags: feature, sql
- :tickets: 3172
-
- Exception messages have been spiffed up a bit. The SQL statement
- and parameters are not displayed if None, reducing confusion for
- error messages that weren't related to a statement. The full
- module and classname for the DBAPI-level exception is displayed,
- making it clear that this is a wrapped DBAPI exception. The
- statement and parameters themselves are bounded within a bracketed
- sections to better isolate them from the error message and from
- each other.
-
- .. change::
- :tags: bug, orm
- :tickets: 3228
-
- The :meth:`_query.Query.update` method will now convert string key
- names in the given dictionary of values into mapped attribute names
- against the mapped class being updated. Previously, string names
- were taken in directly and passed to the core update statement without
- any means to resolve against the mapped entity. Support for synonyms
- and hybrid attributes as the subject attributes of
- :meth:`_query.Query.update` are also supported.
-
- .. seealso::
-
- :ref:`bug_3228`
-
- .. change::
- :tags: bug, orm
- :tickets: 3035
-
- Improvements to the mechanism used by :class:`.Session` to locate
- "binds" (e.g. engines to use), such engines can be associated with
- mixin classes, concrete subclasses, as well as a wider variety
- of table metadata such as joined inheritance tables.
-
- .. seealso::
-
- :ref:`bug_3035`
-
- .. change::
- :tags: bug, general
- :tickets: 3218
-
- The ``__module__`` attribute is now set for all those SQL and
- ORM functions that are derived as "public factory" symbols, which
- should assist with documentation tools being able to report on the
- target module.
-
- .. change::
- :tags: feature, sql
-
- :meth:`_expression.Insert.from_select` now includes Python and SQL-expression
- defaults if otherwise unspecified; the limitation where non-
- server column defaults aren't included in an INSERT FROM
- SELECT is now lifted and these expressions are rendered as
- constants into the SELECT statement.
-
- .. seealso::
-
- :ref:`feature_insert_from_select_defaults`
-
- .. change::
- :tags: bug, orm
- :tickets: 3233
-
- Fixed bug in single table inheritance where a chain of joins
- that included the same single inh entity more than once
- (normally this should raise an error) could, in some cases
- depending on what was being joined "from", implicitly alias the
- second case of the single inh entity, producing
- a query that "worked". But as this implicit aliasing is not
- intended in the case of single table inheritance, it didn't
- really "work" fully and was very misleading, since it wouldn't
- always appear.
-
- .. seealso::
-
- :ref:`bug_3233`
-
-
- .. change::
- :tags: bug, orm
- :tickets: 3222
-
- The ON clause rendered when using :meth:`_query.Query.join`,
- :meth:`_query.Query.outerjoin`, or the standalone :func:`_orm.join` /
- :func:`_orm.outerjoin` functions to a single-inheritance subclass will
- now include the "single table criteria" in the ON clause even
- if the ON clause is otherwise hand-rolled; it is now added to the
- criteria using AND, the same way as if joining to a single-table
- target using relationship or similar.
-
- This is sort of in-between feature and bug.
-
- .. seealso::
-
- :ref:`migration_3222`
-
- .. change::
- :tags: feature, sql
- :tickets: 3184
-
- The :class:`.UniqueConstraint` construct is now included when
- reflecting a :class:`_schema.Table` object, for databases where this
- is applicable. In order to achieve this
- with sufficient accuracy, MySQL and PostgreSQL now contain features
- that correct for the duplication of indexes and unique constraints
- when reflecting tables, indexes, and constraints.
- In the case of MySQL, there is not actually a "unique constraint"
- concept independent of a "unique index", so for this backend
- :class:`.UniqueConstraint` continues to remain non-present for a
- reflected :class:`_schema.Table`. For PostgreSQL, the query used to
- detect indexes against ``pg_index`` has been improved to check for
- the same construct in ``pg_constraint``, and the implicitly
- constructed unique index is not included with a
- reflected :class:`_schema.Table`.
-
- In both cases, the :meth:`_reflection.Inspector.get_indexes` and the
- :meth:`_reflection.Inspector.get_unique_constraints` methods return both
- constructs individually, but include a new token
- ``duplicates_constraint`` in the case of PostgreSQL or
- ``duplicates_index`` in the case
- of MySQL to indicate when this condition is detected.
- Pull request courtesy Johannes Erdfelt.
-
- .. seealso::
-
- :ref:`feature_3184`
-
- .. change::
- :tags: feature, postgresql
-
- Added support for the FILTER keyword as applied to aggregate
- functions, supported by PostgreSQL 9.4. Pull request
- courtesy Ilja Everilä.
-
- .. seealso::
-
- :ref:`feature_gh134`
-
- .. change::
- :tags: bug, sql, engine
- :tickets: 3215
-
- Fixed bug where a "branched" connection, that is the kind you get
- when you call :meth:`_engine.Connection.connect`, would not share invalidation
- status with the parent. The architecture of branching has been tweaked
- a bit so that the branched connection defers to the parent for
- all invalidation status and operations.
-
- .. change::
- :tags: bug, sql, engine
- :tickets: 3190
-
- Fixed bug where a "branched" connection, that is the kind you get
- when you call :meth:`_engine.Connection.connect`, would not share transaction
- status with the parent. The architecture of branching has been tweaked
- a bit so that the branched connection defers to the parent for
- all transactional status and operations.
-
- .. change::
- :tags: bug, orm, declarative
- :tickets: 2670
-
- A relationship set up with :class:`.declared_attr` on
- a :class:`.AbstractConcreteBase` base class will now be configured
- on the abstract base mapping automatically, in addition to being
- set up on descendant concrete classes as usual.
-
- .. seealso::
-
- :ref:`feature_3150`
-
- .. change::
- :tags: feature, orm, declarative
- :tickets: 3150
-
- The :class:`.declared_attr` construct has newly improved
- behaviors and features in conjunction with declarative. The
- decorated function will now have access to the final column
- copies present on the local mixin when invoked, and will also
- be invoked exactly once for each mapped class, the returned result
- being memoized. A new modifier :attr:`.declared_attr.cascading`
- is added as well.
-
- .. seealso::
-
- :ref:`feature_3150`
-
- .. change::
- :tags: feature, ext
- :tickets: 3210
-
- The :mod:`sqlalchemy.ext.automap` extension will now set
- ``cascade="all, delete-orphan"`` automatically on a one-to-many
- relationship/backref where the foreign key is detected as containing
- one or more non-nullable columns. This argument is present in the
- keywords passed to :func:`.automap.generate_relationship` in this
- case and can still be overridden. Additionally, if the
- :class:`_schema.ForeignKeyConstraint` specifies ``ondelete="CASCADE"``
- for a non-nullable or ``ondelete="SET NULL"`` for a nullable set
- of columns, the argument ``passive_deletes=True`` is also added to the
- relationship. Note that not all backends support reflection of
- ondelete, but backends that do include PostgreSQL and MySQL.
-
- .. change::
- :tags: feature, sql
- :tickets: 3206
-
- Added new method :meth:`_expression.Select.with_statement_hint` and ORM
- method :meth:`_query.Query.with_statement_hint` to support statement-level
- hints that are not specific to a table.
-
- .. change::
- :tags: bug, sqlite
- :tickets: 3203
-
- SQLite now supports reflection of unique constraints from
- temp tables; previously, this would fail with a TypeError.
- Pull request courtesy Johannes Erdfelt.
-
- .. seealso::
-
- :ref:`change_3204` - changes regarding SQLite temporary
- table and view reflection.
-
- .. change::
- :tags: bug, sqlite
- :tickets: 3204
-
- Added :meth:`_reflection.Inspector.get_temp_table_names` and
- :meth:`_reflection.Inspector.get_temp_view_names`; currently, only the
- SQLite and Oracle dialects support these methods. The return of
- temporary table and view names has been **removed** from SQLite and
- Oracle's version of :meth:`_reflection.Inspector.get_table_names` and
- :meth:`_reflection.Inspector.get_view_names`; other database backends cannot
- support this information (such as MySQL), and the scope of operation
- is different in that the tables can be local to a session and
- typically aren't supported in remote schemas.
-
- .. seealso::
-
- :ref:`change_3204`
-
- .. change::
- :tags: feature, postgresql
- :tickets: 2891
-
- Support has been added for reflection of materialized views
- and foreign tables, as well as support for materialized views
- within :meth:`_reflection.Inspector.get_view_names`, and a new method
- :meth:`.PGInspector.get_foreign_table_names` available on the
- PostgreSQL version of :class:`_reflection.Inspector`. Pull request courtesy
- Rodrigo Menezes.
-
- .. seealso::
-
- :ref:`feature_2891`
-
-
- .. change::
- :tags: feature, orm
-
- Added new event handlers :meth:`.AttributeEvents.init_collection`
- and :meth:`.AttributeEvents.dispose_collection`, which track when
- a collection is first associated with an instance and when it is
- replaced. These handlers supersede the :meth:`.collection.linker`
- annotation. The old hook remains supported through an event adapter.
-
- .. change::
- :tags: bug, orm
- :tickets: 3148, 3188
-
- A major rework to the behavior of expression labels, most
- specifically when used with ColumnProperty constructs with
- custom SQL expressions and in conjunction with the "order by
- labels" logic first introduced in 0.9. Fixes include that an
- ``order_by(Entity.some_col_prop)`` will now make use of "order by
- label" rules even if Entity has been subject to aliasing,
- either via inheritance rendering or via the use of the
- ``aliased()`` construct; rendering of the same column property
- multiple times with aliasing (e.g. ``query(Entity.some_prop,
- entity_alias.some_prop)``) will label each occurrence of the
- entity with a distinct label, and additionally "order by
- label" rules will work for both (e.g.
- ``order_by(Entity.some_prop, entity_alias.some_prop)``).
- Additional issues that could prevent the "order by label"
- logic from working in 0.9, most notably that the state of a
- Label could change such that "order by label" would stop
- working depending on how things were called, has been fixed.
-
- .. seealso::
-
- :ref:`bug_3188`
-
-
- .. change::
- :tags: bug, mysql
- :tickets: 3186
-
- MySQL boolean symbols "true", "false" work again. 0.9's change
- in :ticket:`2682` disallowed the MySQL dialect from making use of the
- "true" and "false" symbols in the context of "IS" / "IS NOT", but
- MySQL supports this syntax even though it has no boolean type.
- MySQL remains "non native boolean", but the :func:`.true`
- and :func:`.false` symbols again produce the
- keywords "true" and "false", so that an expression like
- ``column.is_(true())`` again works on MySQL.
-
- .. seealso::
-
- :ref:`bug_3186`
-
- .. change::
- :tags: changed, mssql
- :tickets: 3182
-
- The hostname-based connection format for SQL Server when using
- pyodbc will no longer specify a default "driver name", and a warning
- is emitted if this is missing. The optimal driver name for SQL Server
- changes frequently and is per-platform, so hostname based connections
- need to specify this. DSN-based connections are preferred.
-
- .. seealso::
-
- :ref:`change_3182`
-
- .. change::
- :tags: changed, sql
-
- The :func:`_expression.column` and :func:`_expression.table`
- constructs are now importable from the "from sqlalchemy" namespace,
- just like every other Core construct.
-
- .. change::
- :tags: changed, sql
- :tickets: 2992
-
- The implicit conversion of strings to :func:`_expression.text` constructs
- when passed to most builder methods of :func:`_expression.select` as
- well as :class:`_query.Query` now emits a warning with just the
- plain string sent. The textual conversion still proceeds normally,
- however. The only method that accepts a string without a warning
- are the "label reference" methods like order_by(), group_by();
- these functions will now at compile time attempt to resolve a single
- string argument to a column or label expression present in the
- selectable; if none is located, the expression still renders, but
- you get the warning again. The rationale here is that the implicit
- conversion from string to text is more unexpected than not these days,
- and it is better that the user send more direction to the Core / ORM
- when passing a raw string as to what direction should be taken.
- Core/ORM tutorials have been updated to go more in depth as to how text
- is handled.
-
- .. seealso::
-
- :ref:`migration_2992`
-
-
- .. change::
- :tags: feature, engine
- :tickets: 3178
-
- A new style of warning can be emitted which will "filter" up to
- N occurrences of a parameterized string. This allows parameterized
- warnings that can refer to their arguments to be delivered a fixed
- number of times until allowing Python warning filters to squelch them,
- and prevents memory from growing unbounded within Python's
- warning registries.
-
- .. seealso::
-
- :ref:`feature_3178`
-
- .. change::
- :tags: feature, orm
-
- The :class:`_query.Query` will raise an exception when :meth:`_query.Query.yield_per`
- is used with mappings or options where either
- subquery eager loading, or joined eager loading with collections,
- would take place. These loading strategies are
- not currently compatible with yield_per, so by raising this error,
- the method is safer to use. Eager loads can be disabled with
- the ``lazyload('*')`` option or :meth:`_query.Query.enable_eagerloads`.
-
- .. seealso::
-
- :ref:`migration_yield_per_eager_loading`
-
- .. change::
- :tags: bug, orm
- :tickets: 3177
-
- Changed the approach by which the "single inheritance criterion"
- is applied, when using :meth:`_query.Query.from_self`, or its common
- user :meth:`_query.Query.count`. The criteria to limit rows to those
- with a certain type is now indicated on the inside subquery,
- not the outside one, so that even if the "type" column is not
- available in the columns clause, we can filter on it on the "inner"
- query.
-
- .. seealso::
-
- :ref:`migration_3177`
-
- .. change::
- :tags: changed, orm
-
- The ``proc()`` callable passed to the ``create_row_processor()``
- method of custom :class:`.Bundle` classes now accepts only a single
- "row" argument.
-
- .. seealso::
-
- :ref:`bundle_api_change`
-
- .. change::
- :tags: changed, orm
-
- Deprecated event hooks removed: ``populate_instance``,
- ``create_instance``, ``translate_row``, ``append_result``
-
- .. seealso::
-
- :ref:`migration_deprecated_orm_events`
-
- .. change::
- :tags: bug, orm
- :tickets: 3145
-
- Made a small adjustment to the mechanics of lazy loading,
- such that it has less chance of interfering with a joinload() in the
- very rare circumstance that an object points to itself; in this
- scenario, the object refers to itself while loading its attributes
- which can cause a mixup between loaders. The use case of
- "object points to itself" is not fully supported, but the fix also
- removes some overhead so for now is part of testing.
-
- .. change::
- :tags: feature, orm
- :tickets: 3176
-
- A new implementation for :class:`.KeyedTuple` used by the
- :class:`_query.Query` object offers dramatic speed improvements when
- fetching large numbers of column-oriented rows.
-
- .. seealso::
-
- :ref:`feature_3176`
-
- .. change::
- :tags: feature, orm
- :tickets: 3008
-
- The behavior of :paramref:`_orm.joinedload.innerjoin` as well as
- :paramref:`_orm.relationship.innerjoin` is now to use "nested"
- inner joins, that is, right-nested, as the default behavior when an
- inner join joined eager load is chained to an outer join eager load.
-
- .. seealso::
-
- :ref:`migration_3008`
-
- .. change::
- :tags: bug, orm
- :tickets: 3171
-
- The "resurrect" ORM event has been removed. This event hook had
- no purpose since the old "mutable attribute" system was removed
- in 0.8.
-
- .. change::
- :tags: bug, sql
- :tickets: 3169
-
- Using :meth:`_expression.Insert.from_select` now implies ``inline=True``
- on :func:`_expression.insert`. This helps to fix a bug where an
- INSERT...FROM SELECT construct would inadvertently be compiled
- as "implicit returning" on supporting backends, which would
- cause breakage in the case of an INSERT that inserts zero rows
- (as implicit returning expects a row), as well as arbitrary
- return data in the case of an INSERT that inserts multiple
- rows (e.g. only the first row of many).
- A similar change is also applied to an INSERT..VALUES
- with multiple parameter sets; implicit RETURNING will no longer emit
- for this statement either. As both of these constructs deal
- with variable numbers of rows, the
- :attr:`_engine.ResultProxy.inserted_primary_key` accessor does not
- apply. Previously, there was a documentation note that one
- may prefer ``inline=True`` with INSERT..FROM SELECT as some databases
- don't support returning and therefore can't do "implicit" returning,
- but there's no reason an INSERT...FROM SELECT needs implicit returning
- in any case. Regular explicit :meth:`_expression.Insert.returning` should
- be used to return variable numbers of result rows if inserted
- data is needed.
-
- .. change::
- :tags: bug, orm
- :tickets: 3167
-
- Fixed bug where attribute "set" events or columns with
- ``@validates`` would have events triggered within the flush process,
- when those columns were the targets of a "fetch and populate"
- operation, such as an autoincremented primary key, a Python side
- default, or a server-side default "eagerly" fetched via RETURNING.
-
- .. change::
- :tags: feature, oracle
-
- Added support for the Oracle table option ON COMMIT.
-
- .. change::
- :tags: feature, postgresql
- :tickets: 2051
-
- Added support for PG table options TABLESPACE, ON COMMIT,
- WITH(OUT) OIDS, and INHERITS, when rendering DDL via
- the :class:`_schema.Table` construct. Pull request courtesy
- malikdiarra.
-
- .. seealso::
-
- :ref:`postgresql_table_options`
-
- .. change::
- :tags: bug, orm, py3k
-
- The :class:`.IdentityMap` exposed from :attr:`.Session.identity_map`
- now returns lists for ``items()`` and ``values()`` in Py3K.
- Early porting to Py3K here had these returning iterators, when
- they technically should be "iterable views"..for now, lists are OK.
-
- .. change::
- :tags: orm, feature
-
- UPDATE statements can now be batched within an ORM flush
- into more performant executemany() call, similarly to how INSERT
- statements can be batched; this will be invoked within flush
- to the degree that subsequent UPDATE statements for the
- same mapping and table involve the identical columns within the
- VALUES clause, that no SET-level SQL expressions
- are embedded, and that the versioning requirements for the mapping
- are compatible with the backend dialect's ability to return
- a correct rowcount for an executemany operation.
-
- .. change::
- :tags: engine, bug
- :tickets: 3163
-
- Removing (or adding) an event listener at the same time that the event
- is being run itself, either from inside the listener or from a
- concurrent thread, now raises a RuntimeError, as the collection used is
- now an instance of ``collections.deque()`` and does not support changes
- while being iterated. Previously, a plain Python list was used where
- removal from inside the event itself would produce silent failures.
-
- .. change::
- :tags: orm, feature
- :tickets: 2963
-
- The ``info`` parameter has been added to the constructor for
- :class:`.SynonymProperty` and :class:`.ComparableProperty`.
-
- .. change::
- :tags: sql, feature
- :tickets: 2963
-
- The ``info`` parameter has been added as a constructor argument
- to all schema constructs including :class:`_schema.MetaData`,
- :class:`.Index`, :class:`_schema.ForeignKey`, :class:`_schema.ForeignKeyConstraint`,
- :class:`.UniqueConstraint`, :class:`.PrimaryKeyConstraint`,
- :class:`.CheckConstraint`.
-
- .. change::
- :tags: orm, feature
- :tickets: 2971
-
- The :attr:`.InspectionAttr.info` collection is now moved down to
- :class:`.InspectionAttr`, where in addition to being available
- on all :class:`.MapperProperty` objects, it is also now available
- on hybrid properties, association proxies, when accessed via
- :attr:`_orm.Mapper.all_orm_descriptors`.
-
- .. change::
- :tags: sql, feature
- :tickets: 3027
-
- The :paramref:`_schema.Table.autoload_with` flag now implies that
- :paramref:`_schema.Table.autoload` should be ``True``. Pull request
- courtesy Malik Diarra.
-
- .. change::
- :tags: postgresql, feature
-
- Added new method :meth:`.PGInspector.get_enums`, when using the
- inspector for PostgreSQL will provide a list of ENUM types.
- Pull request courtesy Ilya Pekelny.
-
- .. change::
- :tags: mysql, bug
-
- The MySQL dialect will now disable :meth:`_events.ConnectionEvents.handle_error`
- events from firing for those statements which it uses internally
- to detect if a table exists or not. This is achieved using an
- execution option ``skip_user_error_events`` that disables the handle
- error event for the scope of that execution. In this way, user code
- that rewrites exceptions doesn't need to worry about the MySQL
- dialect or other dialects that occasionally need to catch
- SQLAlchemy specific exceptions.
-
- .. change::
- :tags: mysql, bug
- :tickets: 2515
-
- Changed the default value of "raise_on_warnings" to False for
- MySQLconnector. This was set at True for some reason. The "buffered"
- flag unfortunately must stay at True as MySQLconnector does not allow
- a cursor to be closed unless all results are fully fetched.
-
- .. change::
- :tags: bug, orm
- :tickets: 3117
-
- The "evaluator" for query.update()/delete() won't work with multi-table
- updates, and needs to be set to `synchronize_session=False` or
- `synchronize_session='fetch'`; this now raises an exception, with a
- message to change the synchronize setting.
- This is upgraded from a warning emitted as of 0.9.7.
-
- .. change::
- :tags: removed
-
- The Drizzle dialect has been removed from the Core; it is now
- available as `sqlalchemy-drizzle <https://bitbucket.org/zzzeek/sqlalchemy-drizzle>`_,
- an independent, third party dialect. The dialect is still based
- almost entirely off of the MySQL dialect present in SQLAlchemy.
-
- .. seealso::
-
- :ref:`change_2984`
-
- .. change::
- :tags: enhancement, orm
- :tickets: 3061
-
- Adjustment to attribute mechanics concerning when a value is
- implicitly initialized to None via first access; this action,
- which has always resulted in a population of the attribute,
- no longer does so; the None value is returned but the underlying
- attribute receives no set event. This is consistent with how collections
- work and allows attribute mechanics to behave more consistently;
- in particular, getting an attribute with no value does not squash
- the event that should proceed if the value is actually set to None.
-
- .. seealso::
-
- :ref:`migration_3061`
-
- .. change::
- :tags: feature, sql
- :tickets: 3034
-
- The :meth:`_expression.Select.limit` and :meth:`_expression.Select.offset` methods
- now accept any SQL expression, in addition to integer values, as
- arguments. Typically this is used to allow a bound parameter to be
- passed, which can be substituted with a value later thus allowing
- Python-side caching of the SQL query. The implementation
- here is fully backwards compatible with existing third party dialects,
- however those dialects which implement special LIMIT/OFFSET systems
- will need modification in order to take advantage of the new
- capabilities. Limit and offset also support "literal_binds" mode,
- where bound parameters are rendered inline as strings based on
- a compile-time option.
- Work on this feature is courtesy of Dobes Vandermeer.
-
-
- .. seealso::
-
- :ref:`feature_3034`.
+++ /dev/null
-=============
-1.1 Changelog
-=============
-
-.. changelog_imports::
-
- .. include:: changelog_10.rst
- :start-line: 5
-
-
- .. include:: changelog_09.rst
- :start-line: 5
-
-
- .. include:: changelog_08.rst
- :start-line: 5
-
-
- .. include:: changelog_07.rst
- :start-line: 5
-
-
-.. changelog::
- :version: 1.1.18
- :released: March 6, 2018
-
- .. change::
- :tags: bug, mysql
- :tickets: 4205
- :versions: 1.2.5
-
- MySQL dialects now query the server version using ``SELECT @@version``
- explicitly to the server to ensure we are getting the correct version
- information back. Proxy servers like MaxScale interfere with the value
- that is passed to the DBAPI's connection.server_version value so this
- is no longer reliable.
-
- .. change::
- :tags: bug, postgresql, py3k
- :tickets: 4208
- :versions: 1.2.5
-
- Fixed bug in PostgreSQL COLLATE / ARRAY adjustment first introduced
- in :ticket:`4006` where new behaviors in Python 3.7 regular expressions
- caused the fix to fail.
-
-.. changelog::
- :version: 1.1.17
- :released: February 22, 2018
-
- .. change::
- :tags: bug, ext
- :tickets: 4185
-
- Repaired regression caused in 1.2.3 and 1.1.16 regarding association proxy
- objects, revising the approach to :ticket:`4185` when calculating the
- "owning class" of an association proxy to default to choosing the current
- class if the proxy object is not directly associated with a mapped class,
- such as a mixin.
-
-.. changelog::
- :version: 1.1.16
- :released: February 16, 2018
-
- .. change::
- :tags: bug, postgresql
- :versions: 1.2.3
-
- Added "SSL SYSCALL error: Operation timed out" to the list
- of messages that trigger a "disconnect" scenario for the
- psycopg2 driver. Pull request courtesy André Cruz.
-
- .. change::
- :tags: bug, orm
- :tickets: 4187
- :versions: 1.2.3
-
- Fixed issue in post_update feature where an UPDATE is emitted
- when the parent object has been deleted but the dependent object
- is not. This issue has existed for a long time however
- since 1.2 now asserts rows matched for post_update, this
- was raising an error.
-
- .. change::
- :tags: bug, mysql
- :tickets: 4136
- :versions: 1.2.0b4
-
- Fixed bug where the MySQL "concat" and "match" operators failed to
- propagate kwargs to the left and right expressions, causing compiler
- options such as "literal_binds" to fail.
-
- .. change::
- :tags: bug, sql
- :versions: 1.2.0b4
-
- Added :func:`.nullsfirst` and :func:`.nullslast` as top level imports
- in the ``sqlalchemy.`` and ``sqlalchemy.sql.`` namespace. Pull request
- courtesy Lele Gaifax.
-
- .. change::
- :tags: bug, orm
- :tickets: 4185
- :versions: 1.2.3
-
- Fixed regression caused by fix for issue :ticket:`4116` affecting versions
- 1.2.2 as well as 1.1.15, which had the effect of mis-calculation of the
- "owning class" of an :class:`.AssociationProxy` as the ``NoneType`` class
- in some declarative mixin/inheritance situations as well as if the
- association proxy were accessed off of an un-mapped class. The "figure out
- the owner" logic has been replaced by an in-depth routine that searches
- through the complete mapper hierarchy assigned to the class or subclass to
- determine the correct (we hope) match; will not assign the owner if no
- match is found. An exception is now raised if the proxy is used
- against an un-mapped instance.
-
-
- .. change::
- :tags: bug, sql
- :tickets: 4162
- :versions: 1.2.1
-
- Fixed bug in :meth:`_expression.Insert.values` where using the "multi-values"
- format in combination with :class:`_schema.Column` objects as keys rather
- than strings would fail. Pull request courtesy Aubrey Stark-Toller.
-
- .. change::
- :tags: bug, postgresql
- :versions: 1.2.3
-
- Added "TRUNCATE" to the list of keywords accepted by the
- PostgreSQL dialect as an "autocommit"-triggering keyword.
- Pull request courtesy Jacob Hayes.
-
- .. change::
- :tags: bug, pool
- :tickets: 4184
- :versions: 1.2.3
-
- Fixed a fairly serious connection pool bug where a connection that is
- acquired after being refreshed as a result of a user-defined
- :class:`_exc.DisconnectionError` or due to the 1.2-released "pre_ping" feature
- would not be correctly reset if the connection were returned to the pool by
- weakref cleanup (e.g. the front-facing object is garbage collected); the
- weakref would still refer to the previously invalidated DBAPI connection
- which would have the reset operation erroneously called upon it instead.
- This would lead to stack traces in the logs and a connection being checked
- into the pool without being reset, which can cause locking issues.
-
-
- .. change::
- :tags: bug, orm
- :tickets: 4151
- :versions: 1.2.1
-
- Fixed bug where an object that is expunged during a rollback of
- a nested or subtransaction which also had its primary key mutated
- would not be correctly removed from the session, causing subsequent
- issues in using the session.
-
-.. changelog::
- :version: 1.1.15
- :released: November 3, 2017
-
- .. change::
- :tags: bug, sqlite
- :tickets: 4099
- :versions: 1.2.0b3
-
- Fixed bug where SQLite CHECK constraint reflection would fail
- if the referenced table were in a remote schema, e.g. on SQLite a
- remote database referred to by ATTACH.
-
- .. change::
- :tags: bug, mysql
- :tickets: 4097
- :versions: 1.2.0b3
-
- Warning emitted when MariaDB 10.2.8 or earlier in the 10.2
- series is detected as there are major issues with CHECK
- constraints within these versions that were resolved as of
- 10.2.9.
-
- Note that this changelog message was NOT released with
- SQLAlchemy 1.2.0b3 and was added retroactively.
-
- .. change::
- :tags: bug, mssql
- :tickets: 4095
- :versions: 1.2.0b3
-
- Added a full range of "connection closed" exception codes to the
- PyODBC dialect for SQL Server, including '08S01', '01002', '08003',
- '08007', '08S02', '08001', 'HYT00', 'HY010'. Previously, only '08S01'
- was covered.
-
- .. change::
- :tags: bug, sql
- :tickets: 4126
- :versions: 1.2.0
-
- Fixed bug where ``__repr__`` of :class:`.ColumnDefault` would fail
- if the argument were a tuple. Pull request courtesy Nicolas Caniart.
-
- .. change::
- :tags: bug, orm, declarative
- :tickets: 4124
- :versions: 1.2.0
-
- Fixed bug where a descriptor that is elsewhere a mapped column
- or relationship within a hierarchy based on :class:`.AbstractConcreteBase`
- would be referred towards during a refresh operation, causing an error
- as the attribute is not mapped as a mapper property.
- A similar issue can arise for other attributes like the "type" column
- added by :class:`.AbstractConcreteBase` if the class fails to include
- "concrete=True" in its mapper, however the check here should also
- prevent that scenario from causing a problem.
-
- .. change:: 4006
- :tags: bug, postgresql
- :tickets: 4006
- :versions: 1.2.0b3
-
- Made further fixes to the :class:`_types.ARRAY` class in conjunction with
- COLLATE, as the fix made in :ticket:`4006` failed to accommodate
- for a multidimensional array.
-
- .. change::
- :tags: bug, orm, ext
- :tickets: 4116
- :versions: 1.2.0
-
- Fixed bug where the association proxy would inadvertently link itself
- to an :class:`.AliasedClass` object if it were called first with
- the :class:`.AliasedClass` as a parent, causing errors upon subsequent
- usage.
-
- .. change::
- :tags: bug, mysql
- :tickets: 4120
- :versions: 1.2.0
-
- MySQL 5.7.20 now warns for use of the @tx_isolation variable; a version
- check is now performed and uses @transaction_isolation instead
- to prevent this warning.
-
- .. change::
- :tags: bug, postgresql
- :tickets: 4107
- :versions: 1.2.0b3
-
- Fixed bug in :obj:`_functions.array_agg` function where passing an argument
- that is already of type :class:`_types.ARRAY`, such as a PostgreSQL
- :obj:`_postgresql.array` construct, would produce a ``ValueError``, due
- to the function attempting to nest the arrays.
-
- .. change::
- :tags: bug, orm
- :tickets: 4078
- :versions: 1.2.0b3
-
- Fixed bug where ORM relationship would warn against conflicting sync
- targets (e.g. two relationships would both write to the same column) for
- sibling classes in an inheritance hierarchy, where the two relationships
- would never actually conflict during writes.
-
- .. change::
- :tags: bug, postgresql
- :tickets: 4074
- :versions: 1.2.0b3
-
- Fixed bug in PostgreSQL :meth:`.postgresql.dml.Insert.on_conflict_do_update`
- which would prevent the insert statement from being used as a CTE,
- e.g. via :meth:`_expression.Insert.cte`, within another statement.
-
- .. change::
- :tags: bug, orm
- :tickets: 4103
- :versions: 1.2.0b3
-
- Fixed bug where correlated select used against single-table inheritance
- entity would fail to render correctly in the outer query, due to adjustment
- for single inheritance discriminator criteria inappropriately re-applying
- the criteria to the outer query.
-
- .. change::
- :tags: bug, mysql
- :tickets: 4096
- :versions: 1.2.0b3
-
- Fixed issue where CURRENT_TIMESTAMP would not reflect correctly
- in the MariaDB 10.2 series due to a syntax change, where the function
- is now represented as ``current_timestamp()``.
-
- .. change::
- :tags: bug, mysql
- :tickets: 4098
- :versions: 1.2.0b3
-
- MariaDB 10.2 now supports CHECK constraints (warning: use version 10.2.9
- or greater due to upstream issues noted in :ticket:`4097`). Reflection
- now takes these CHECK constraints into account when they are present in
- the ``SHOW CREATE TABLE`` output.
-
- .. change::
- :tags: bug, sql
- :tickets: 4093
- :versions: 1.2.0b3
-
- Fixed bug where the recently added :meth:`.ColumnOperators.any_`
- and :meth:`.ColumnOperators.all_` methods didn't work when called
- as methods, as opposed to using the standalone functions
- :func:`_expression.any_` and :func:`_expression.all_`. Also
- added documentation examples for these relatively unintuitive
- SQL operators.
-
-.. changelog::
- :version: 1.1.14
- :released: September 5, 2017
-
- .. change::
- :tags: bug, orm
- :tickets: 4069
- :versions: 1.2.0b3
-
- Fixed bug in :meth:`.Session.merge` following along similar lines as that
- of :ticket:`4030`, where an internal check for a target object in
- the identity map could lead to an error if it were to be garbage collected
- immediately before the merge routine actually retrieves the object.
-
- .. change::
- :tags: bug, orm
- :tickets: 4048
- :versions: 1.2.0b3
-
- Fixed bug where an :func:`.undefer_group` option would not be recognized
- if it extended from a relationship that was loading using joined eager
- loading. Additionally, as the bug led to excess work being performed,
- Python function call counts are also improved by 20% within the initial
- calculation of result set columns, complementing the joined eager load
- improvements of :ticket:`3915`.
-
- .. change::
- :tags: bug, orm
- :tickets: 4068
-
- Fixed race condition in ORM identity map which would cause objects
- to be inappropriately removed during a load operation, causing
- duplicate object identities to occur, particularly under joined eager
- loading which involves deduplication of objects. The issue is specific
- to garbage collection of weak references and is observed only under the
- PyPy interpreter.
-
- .. change::
- :tags: bug, orm
- :tickets: 4056
- :versions: 1.2.0b3
-
- Fixed bug in :meth:`.Session.merge` where objects in a collection that had
- the primary key attribute set to ``None`` for a key that is typically
- autoincrementing would be considered to be a database-persisted key for
- part of the internal deduplication process, causing only one object to
- actually be inserted in the database.
-
- .. change::
- :tags: bug, sql
- :tickets: 4053
-
- Altered the range specification for window functions to allow
- for two of the same PRECEDING or FOLLOWING keywords in a range
- by allowing for the left side of the range to be positive
- and for the right to be negative, e.g. (1, 3) is
- "1 FOLLOWING AND 3 FOLLOWING".
-
- .. change::
- :tags: bug, orm
- :tickets: 4067
- :versions: 1.2.0b3
-
- An :class:`.InvalidRequestError` is raised when a :func:`.synonym`
- is used against an attribute that is not against a :class:`.MapperProperty`,
- such as an association proxy. Previously, a recursion overflow would
- occur trying to locate non-existent attributes.
-
-.. changelog::
- :version: 1.1.13
- :released: August 3, 2017
-
-.. changelog::
- :version: 1.1.12
- :released: July 24, 2017
-
- .. change:: cache_order_sequence
- :tags: feature, oracle, postgresql
- :versions: 1.2.0b1
-
- Added new keywords :paramref:`.Sequence.cache` and
- :paramref:`.Sequence.order` to :class:`.Sequence`, to allow rendering
- of the CACHE parameter understood by Oracle and PostgreSQL, and the
- ORDER parameter understood by Oracle. Pull request
- courtesy David Moore.
-
-
- .. change:: 4033
- :tags: bug, orm
- :tickets: 4033
- :versions: 1.2.0b2
-
- Fixed regression from 1.1.11 where adding additional non-entity
- columns to a query that includes an entity with subqueryload
- relationships would fail, due to an inspection added in 1.1.11 as a
- result of :ticket:`4011`.
-
-
- .. change:: 4031
- :tags: bug, orm
- :versions: 1.2.0b2
- :tickets: 4031
-
- Fixed bug involving JSON NULL evaluation logic added in 1.1 as part
- of :ticket:`3514` where the logic would not accommodate ORM
- mapped attributes named differently from the :class:`_schema.Column`
- that was mapped.
-
- .. change:: 4030
- :tags: bug, orm
- :versions: 1.2.0b2
- :tickets: 4030
-
- Added ``KeyError`` checks to all methods within
- :class:`.WeakInstanceDict` where a check for ``key in dict`` is
- followed by indexed access to that key, to guard against a race against
- garbage collection that under load can remove the key from the dict
- after the code assumes its present, leading to very infrequent
- ``KeyError`` raises.
-
-.. changelog::
- :version: 1.1.11
- :released: Monday, June 19, 2017
-
- .. change:: 4012
- :tags: bug, sql
- :tickets: 4012
- :versions: 1.2.0b1
-
- Fixed AttributeError which would occur in :class:`.WithinGroup`
- construct during an iteration of the structure.
-
- .. change:: 4011
- :tags: bug, orm
- :tickets: 4011
- :versions: 1.2.0b1
-
- Fixed issue with subquery eagerloading which continues on from
- the series of issues fixed in :ticket:`2699`, :ticket:`3106`,
- :ticket:`3893` involving that the "subquery" contains the correct
- FROM clause when beginning from a joined inheritance subclass
- and then subquery eager loading onto a relationship from
- the base class, while the query also includes criteria against
- the subclass. The fix in the previous tickets did not accommodate
- for additional subqueryload operations loading more deeply from
- the first level, so the fix has been further generalized.
-
- .. change:: 4005
- :tags: bug, postgresql
- :tickets: 4005
- :versions: 1.2.0b1
-
- Continuing with the fix that correctly handles PostgreSQL
- version string "10devel" released in 1.1.8, an additional regexp
- bump to handle version strings of the form "10beta1". While
- PostgreSQL now offers better ways to get this information, we
- are sticking w/ the regexp at least through 1.1.x for the least
- amount of risk to compatibility w/ older or alternate PostgreSQL
- databases.
-
- .. change:: 4006
- :tags: bug, postgresql
- :tickets: 4006
- :versions: 1.2.0b1
-
- Fixed bug where using :class:`_types.ARRAY` with a string type that
- features a collation would fail to produce the correct syntax
- within CREATE TABLE.
-
- .. change:: 4007
- :tags: bug, mysql
- :tickets: 4007
- :versions: 1.2.0b1
-
- MySQL 5.7 has introduced permission limiting for the "SHOW VARIABLES"
- command; the MySQL dialect will now handle when SHOW returns no
- row, in particular for the initial fetch of SQL_MODE, and will
- emit a warning that user permissions should be modified to allow the
- row to be present.
-
- .. change:: 3994
- :tags: bug, mssql
- :tickets: 3994
- :versions: 1.2.0b1
-
- Fixed bug where SQL Server transaction isolation must be fetched
- from a different view when using Azure data warehouse, the query
- is now attempted against both views and then a NotImplemented
- is raised unconditionally if failure continues to provide the
- best resiliency against future arbitrary API changes in new
- SQL Server versions.
-
- .. change:: 3997
- :tags: bug, oracle
- :tickets: 3997
- :versions: 1.2.0b1
-
- Support for two-phase transactions has been removed entirely for
- cx_Oracle when version 6.0b1 or later of the DBAPI is in use. The two-
- phase feature historically has never been usable under cx_Oracle 5.x in
- any case, and cx_Oracle 6.x has removed the connection-level "twophase"
- flag upon which this feature relied.
-
- .. change:: 3973
- :tags: bug, mssql
- :tickets: 3973
- :versions: 1.2.0b1
-
- Added a placeholder type :class:`_mssql.XML` to the SQL Server
- dialect, so that a reflected table which includes this type can
- be re-rendered as a CREATE TABLE. The type has no special round-trip
- behavior nor does it currently support additional qualifying
- arguments.
-
-.. changelog::
- :version: 1.1.10
- :released: Friday, May 19, 2017
-
- .. change:: 3986
- :tags: bug, orm
- :versions: 1.2.0b1
- :tickets: 3986
-
- Fixed bug where a cascade such as "delete-orphan" (but others as well)
- would fail to locate an object linked to a relationship that itself
- is local to a subclass in an inheritance relationship, thus causing
- the operation to not take place.
-
- .. change:: 3975
- :tags: bug, oracle
- :versions: 1.2.0b1
- :tickets: 3975
-
- Fixed bug in cx_Oracle dialect where version string parsing would
- fail for cx_Oracle version 6.0b1 due to the "b" character. Version
- string parsing is now via a regexp rather than a simple split.
-
- .. change:: 3949
- :tags: bug, schema
- :versions: 1.2.0b1
- :tickets: 3949
-
- An :class:`.ArgumentError` is now raised if a
- :class:`_schema.ForeignKeyConstraint` object is created with a mismatched
- number of "local" and "remote" columns, which otherwise causes the
- internal state of the constraint to be incorrect. Note that this
- also impacts the condition where a dialect's reflection process
- produces a mismatched set of columns for a foreign key constraint.
-
- .. change:: 3980
- :tags: bug, ext
- :versions: 1.2.0b1
- :tickets: 3980
-
- Protected against testing "None" as a class in the case where
- declarative classes are being garbage collected and new
- automap prepare() operations are taking place concurrently, very
- infrequently hitting a weakref that has not been fully acted upon
- after gc.
-
- .. change::
- :tags: bug, postgresql
- :versions: 1.2.0b1
-
- Added "autocommit" support for GRANT, REVOKE keywords. Pull request
- courtesy Jacob Hayes.
-
- .. change:: 3966
- :tags: bug, mysql
- :versions: 1.2.0b1
- :tickets: 3966
-
- Removed an ancient and unnecessary intercept of the UTC_TIMESTAMP
- MySQL function, which was getting in the way of using it with a
- parameter.
-
- .. change:: 3961
- :tags: bug, mysql
- :versions: 1.2.0b1
- :tickets: 3961
-
- Fixed bug in MySQL dialect regarding rendering of table options in
- conjunction with PARTITION options when rendering CREATE TABLE.
- The PARTITION related options need to follow the table options,
- whereas previously this ordering was not enforced.
-
-
-.. changelog::
- :version: 1.1.9
- :released: April 4, 2017
-
- .. change:: 3956
- :tags: bug, ext
- :tickets: 3956
-
- Fixed regression released in 1.1.8 due to :ticket:`3950` where the
- deeper search for information about column types in the case of a
- "schema type" or a :class:`.TypeDecorator` would produce an attribute
- error if the mapping also contained a :obj:`.column_property`.
-
- .. change:: 3952
- :tags: bug, sql
- :versions: 1.2.0b1
- :tickets: 3952
-
- Fixed regression released in 1.1.5 due to :ticket:`3859` where
- adjustments to the "right-hand-side" evaluation of an expression
- based on :class:`.Variant` to honor the underlying type's
- "right-hand-side" rules caused the :class:`.Variant` type
- to be inappropriately lost, in those cases when we *do* want the
- left-hand side type to be transferred directly to the right hand side
- so that bind-level rules can be applied to the expression's argument.
-
- .. change:: 3955
- :tags: bug, sql, postgresql
- :versions: 1.2.0b1
- :tickets: 3955
-
- Changed the mechanics of :class:`_engine.ResultProxy` to unconditionally
- delay the "autoclose" step until the :class:`_engine.Connection` is done
- with the object; in the case where PostgreSQL ON CONFLICT with
- RETURNING returns no rows, autoclose was occurring in this previously
- non-existent use case, causing the usual autocommit behavior that
- occurs unconditionally upon INSERT/UPDATE/DELETE to fail.
-
-.. changelog::
- :version: 1.1.8
- :released: March 31, 2017
-
- .. change:: 3950
- :tags: bug, ext
- :versions: 1.2.0b1
- :tickets: 3950
-
- Fixed bug in :mod:`sqlalchemy.ext.mutable` where the
- :meth:`.Mutable.as_mutable` method would not track a type that had
- been copied using :meth:`.TypeEngine.copy`. This became more of
- a regression in 1.1 compared to 1.0 because the :class:`.TypeDecorator`
- class is now a subclass of :class:`.SchemaEventTarget`, which among
- other things indicates to the parent :class:`_schema.Column` that the type
- should be copied when the :class:`_schema.Column` is. These copies are
- common when using declarative with mixins or abstract classes.
-
- .. change::
- :tags: bug, ext
- :versions: 1.2.0b1
-
- Added support for bound parameters, e.g. those normally set up
- via :meth:`_query.Query.params`, to the :meth:`.baked.Result.count`
- method. Previously, support for parameters were omitted. Pull request
- courtesy Pat Deegan.
-
- .. change::
- :tags: bug, postgresql
- :versions: 1.2.0b1
-
- Added support for parsing the PostgreSQL version string for
- a development version like "PostgreSQL 10devel". Pull request
- courtesy Sean McCully.
-
-.. changelog::
- :version: 1.1.7
- :released: March 27, 2017
-
- .. change::
- :tags: feature, orm
- :tickets: 3933
- :versions: 1.2.0b1
-
- An :func:`.aliased()` construct can now be passed to the
- :meth:`_query.Query.select_entity_from` method. Entities will be pulled
- from the selectable represented by the :func:`.aliased` construct.
- This allows special options for :func:`.aliased` such as
- :paramref:`.aliased.adapt_on_names` to be used in conjunction with
- :meth:`_query.Query.select_entity_from`.
-
- .. change::
- :tags: bug, engine
- :tickets: 3946
- :versions: 1.2.0b1
-
- Added an exception handler that will warn for the "cause" exception on
- Py2K when the "autorollback" feature of :class:`_engine.Connection` itself
- raises an exception. In Py3K, the two exceptions are naturally reported
- by the interpreter as one occurring during the handling of the other.
- This is continuing with the series of changes for rollback failure
- handling that were last visited as part of :ticket:`2696` in 1.0.12.
-
- .. change::
- :tags: bug, orm
- :tickets: 3947
- :versions: 1.2.0b1
-
- Fixed a race condition which could occur under threaded environments
- as a result of the caching added via :ticket:`3915`. An internal
- collection of ``Column`` objects could be regenerated on an alias
- object inappropriately, confusing a joined eager loader when it
- attempts to render SQL and collect results and resulting in an
- attribute error. The collection is now generated up front before
- the alias object is cached and shared among threads.
-
- .. change::
- :tags: bug, sql, postgresql
- :tickets: 2892
-
- Added support for the :class:`.Variant` and the :class:`.SchemaType`
- objects to be compatible with each other. That is, a variant
- can be created against a type like :class:`.Enum`, and the instructions
- to create constraints and/or database-specific type objects will
- propagate correctly as per the variant's dialect mapping.
-
- .. change::
- :tags: bug, sql
- :tickets: 3931
-
- Fixed bug in compiler where the string identifier of a savepoint would
- be cached in the identifier quoting dictionary; as these identifiers
- are arbitrary, a small memory leak could occur if a single
- :class:`_engine.Connection` had an unbounded number of savepoints used,
- as well as if the savepoint clause constructs were used directly
- with an unbounded umber of savepoint names. The memory leak does
- **not** impact the vast majority of cases as normally the
- :class:`_engine.Connection`, which renders savepoint names with a simple
- counter starting at "1", is used on a per-transaction or
- per-fixed-number-of-transactions basis before being discarded.
-
- .. change::
- :tags: bug, sql
- :tickets: 3924
-
- Fixed bug in new "schema translate" feature where the translated schema
- name would be invoked in terms of an alias name when rendered along
- with a column expression; occurred only when the source translate
- name was "None". The "schema translate" feature now only takes
- effect for :class:`.SchemaItem` and :class:`.SchemaType` subclasses,
- that is, objects that correspond to a DDL-creatable structure in
- a database.
-
-.. changelog::
- :version: 1.1.6
- :released: February 28, 2017
-
- .. change::
- :tags: bug, mysql
-
- Added new MySQL 8.0 reserved words to the MySQL dialect for proper
- quoting. Pull request courtesy Hanno Schlichting.
-
- .. change:: 3915
- :tags: bug, orm
- :tickets: 3915
-
- Addressed some long unattended performance concerns within the joined
- eager loader query construction system that have accumulated since
- earlier versions as a result of increased abstraction. The use of ad-
- hoc :class:`.AliasedClass` objects per query, which produces lots of
- column lookup overhead each time, has been replaced with a cached
- approach that makes use of a small pool of :class:`.AliasedClass`
- objects that are reused between invocations of joined eager loading.
- Some mechanics involving eager join path construction have also been
- optimized. Callcounts for an end-to-end query construction + single
- row fetch test with a worst-case joined loader scenario have been
- reduced by about 60% vs. 1.1.5 and 42% vs. that of 0.8.6.
-
- .. change:: 3804
- :tags: bug, postgresql
- :tickets: 3804
-
- Added regular expressions for the "IMPORT FOREIGN SCHEMA",
- "REFRESH MATERIALIZED VIEW" PostgreSQL statements so that they
- autocommit when invoked via a connection or engine without
- an explicit transaction. Pull requests courtesy Frazer McLean
- and Paweł Stiasny.
-
- .. change:: 3909
- :tags: bug, orm
- :tickets: 3909
-
- Fixed a major inefficiency in the "eager_defaults" feature whereby
- an unnecessary SELECT would be emitted for column values where the
- ORM had explicitly inserted NULL, corresponding to attributes that
- were unset on the object but did not have any server default
- specified, as well as expired attributes on update that nevertheless
- had no server onupdate set up. As these columns are not part of the
- RETURNING that eager_defaults tries to use, they should not be
- post-SELECTed either.
-
- .. change:: 3908
- :tags: bug, orm
- :tickets: 3908
-
- Fixed two closely related bugs involving the mapper eager_defaults
- flag in conjunction with single-table inheritance; one where the
- eager defaults logic would inadvertently try to access a column
- that's part of the mapper's "exclude_properties" list (used by
- Declarative with single table inheritance) during the eager defaults
- fetch, and the other where the full load of the row in order to
- fetch the defaults would fail to use the correct inheriting mapper.
-
-
- .. change:: 3905
- :tags: bug, sql
- :tickets: 3905
-
- Fixed bug whereby the :meth:`.DDLEvents.column_reflect` event would not
- allow a non-textual expression to be passed as the value of the
- "default" for the new column, such as a :class:`.FetchedValue`
- object to indicate a generic triggered default or a
- :func:`_expression.text` construct. Clarified the documentation
- in this regard as well.
-
- .. change:: 3901
- :tags: bug, ext
- :tickets: 3901
-
- Fixed bug in new :mod:`sqlalchemy.ext.indexable` extension
- where setting of a property that itself refers to another property
- would fail.
-
- .. change:: 3900
- :tags: bug, postgresql
- :tickets: 3900
-
- Fixed bug in PostgreSQL :class:`.ExcludeConstraint` where the
- "whereclause" and "using" parameters would not be copied during an
- operation like :meth:`_schema.Table.tometadata`.
-
- .. change:: 3898
- :tags: bug, mssql
- :tickets: 3898
-
- Added a version check to the "get_isolation_level" feature, which is
- invoked upon first connect, so that it skips for SQL Server version
- 2000, as the necessary system view is not available prior to SQL Server
- 2005.
-
- .. change:: 3897
- :tags: feature, ext
- :tickets: 3896
-
- Added :meth:`.baked.Result.scalar` and :meth:`.baked.Result.count`
- to the "baked" query system.
-
- .. change:: 3895
- :tags: bug, orm, declarative
- :tickets: 3895
-
- Fixed bug where the "automatic exclude" feature of declarative that
- ensures a column local to a single table inheritance subclass does
- not appear as an attribute on other derivations of the base would
- not take effect for multiple levels of subclassing from the base.
-
- .. change:: 3893
- :tags: bug, orm
- :tickets: 3893
-
- Fixed bug first introduced in 0.9.7 as a result of :ticket:`3106`
- which would cause an incorrect query in some forms of multi-level
- subqueryload against aliased entities, with an unnecessary extra
- FROM entity in the innermost subquery.
-
-.. changelog::
- :version: 1.1.5
- :released: January 17, 2017
-
- .. change:: mysql_index_prefix
- :tags: feature, mysql
-
- Added a new parameter ``mysql_prefix`` supported by the :class:`.Index`
- construct, allows specification of MySQL-specific prefixes such as
- "FULLTEXT". Pull request courtesy Joseph Schorr.
-
- .. change:: 3854
- :tags: bug, orm
- :tickets: 3854
-
- Fixed bug in subquery loading where an object encountered as an
- "existing" row, e.g. already loaded from a different path in the
- same query, would not invoke subquery loaders for unloaded attributes
- that specified this loading. This issue is in the same area
- as that of :ticket:`3431`, :ticket:`3811` which involved
- similar issues with joined loading.
-
- .. change:: 3888
- :tags: bug, postgresql
- :tickets: 3888
-
- Fixed bug in new "ON CONFLICT DO UPDATE" feature where the "set"
- values for the UPDATE clause would not be subject to type-level
- processing, as normally takes effect to handle both user-defined
- type level conversions as well as dialect-required conversions, such
- as those required for JSON datatypes. Additionally, clarified that
- the keys in the ``set_`` dictionary should match the "key" of the
- column, if distinct from the column name. A warning is emitted
- for remaining column names that don't match column keys; for
- compatibility reasons, these are emitted as they were previously.
-
- .. change:: 3872
- :tags: bug, examples
- :tickets: 3872
-
- Fixed two issues with the versioned_history example, one is that
- the history table now gets autoincrement=False to avoid 1.1's new
- errors regarding composite primary keys with autoincrement; the other
- is that the sqlite_autoincrement flag is now used to ensure on SQLite,
- unique identifiers are used for the lifespan of a table even if
- some rows are deleted. Pull request courtesy Carlos García Montoro.
-
- .. change:: 3882
- :tags: bug, sql
- :tickets: 3882
-
- Fixed bug originally introduced in 0.9 via :ticket:`1068` where
- order_by(<some Label()>) would order by the label name based on name
- alone, that is, even if the labeled expression were not at all the same
- expression otherwise present, implicitly or explicitly, in the
- selectable. The logic that orders by label now ensures that the
- labeled expression is related to the one that resolves to that name
- before ordering by the label name; additionally, the name has to
- resolve to an actual label explicit in the expression elsewhere, not
- just a column name. This logic is carefully kept separate from the
- order by(textual name) feature that has a slightly different purpose.
-
- .. change:: try_finally_for_noautoflush
- :tags: bug, orm
-
- The :attr:`.Session.no_autoflush` context manager now ensures that
- the autoflush flag is reset within a "finally" block, so that if
- an exception is raised within the block, the state still resets
- appropriately. Pull request courtesy Emin Arakelian.
-
- .. change:: 3878
- :tags: bug, sql
- :tickets: 3878
-
- Fixed 1.1 regression where "import *" would not work for
- sqlalchemy.sql.expression, due to mis-spelled ``any_`` and ``all_``
- functions.
-
- .. change:: 3880
- :tags: bg, sql
- :tickets: 3880
-
- Fixed bug where literal_binds compiler flag was not honored by the
- :class:`_expression.Insert` construct for the "multiple values" feature; the
- subsequent values are now rendered as literals.
-
- .. change:: 3877
- :tags: bug, oracle, postgresql
- :tickets: 3877
-
- Fixed bug where an INSERT from SELECT where the source table contains
- an autoincrementing Sequence would fail to compile correctly.
-
- .. change:: 3876
- :tags: bug, mssql
- :tickets: 3876
-
- Fixed bug where SQL Server dialects would attempt to select the
- last row identity for an INSERT from SELECT, failing in the case when
- the SELECT has no rows. For such a statement,
- the inline flag is set to True indicating no last primary key
- should be fetched.
-
- .. change:: 3875
- :tags: bug, oracle
- :tickets: 3875
-
- Fixed bug where the "COMPRESSION" keyword was used in the ALL_TABLES
- query on Oracle 9.2; even though Oracle docs state table compression
- was introduced in 9i, the actual column is not present until
- 10.1.
-
- .. change:: 3874
- :tags: bug, orm
- :tickets: 3874
-
- Fixed bug where the single-table inheritance query criteria would not
- be inserted into the query in the case that the :class:`.Bundle`
- construct were used as the selection criteria.
-
- .. change:: repr_for_url_reflect
- :tags: bug, sql
-
- The engine URL embedded in the exception for "could not reflect"
- in :meth:`_schema.MetaData.reflect` now conceals the password; also
- the ``__repr__`` for :class:`.TLEngine` now acts like that of
- :class:`_engine.Engine`, concealing the URL password. Pull request courtesy
- Valery Yundin.
-
- .. change:: 3867
- :tags: bug, mysql
- :tickets: 3867
-
- The MySQL dialect now will not warn when a reflected column has a
- "COMMENT" keyword on it, but note however the comment is not yet
- reflected; this is on the roadmap for a future release. Pull request
- courtesy Lele Long.
-
- .. change:: pg_timestamp_zero_prec
- :tags: bug, postgresql
-
- The :class:`_postgresql.TIME` and :class:`_postgresql.TIMESTAMP`
- datatypes now support a setting of zero for "precision"; previously
- a zero would be ignored. Pull request courtesy Ionuț Ciocîrlan.
-
- .. change:: 3861
- :tags: bug, engine
- :tickets: 3861
-
- The "extend_existing" option of :class:`_schema.Table` reflection would
- cause indexes and constraints to be doubled up in the case that the parameter
- were used with :meth:`_schema.MetaData.reflect` (as the automap extension does)
- due to tables being reflected both within the foreign key path as well
- as directly. A new de-duplicating set is passed through within the
- :meth:`_schema.MetaData.reflect` sequence to prevent double reflection in this
- way.
-
- .. change:: 3859
- :tags: bug, sql
- :tickets: 3859
-
- Fixed issue in :class:`.Variant` where the "right hand coercion" logic,
- inherited from :class:`.TypeDecorator`, would
- coerce the right-hand side into the :class:`.Variant` itself, rather than
- what the default type for the :class:`.Variant` would do. In the
- case of :class:`.Variant`, we want the type to act mostly like the base
- type so the default logic of :class:`.TypeDecorator` is now overridden
- to fall back to the underlying wrapped type's logic. Is mostly relevant
- for JSON at the moment.
-
- .. change:: 3856
- :tags: bug, orm
- :tickets: 3856
-
- Fixed bug related to :ticket:`3177`, where a UNION or other set operation
- emitted by a :class:`_query.Query` would apply "single-inheritance" criteria
- to the outside of the union (also referencing the wrong selectable),
- even though this criteria is now expected to
- be already present on the inside subqueries. The single-inheritance
- criteria is now omitted once union() or another set operation is
- called against :class:`_query.Query` in the same way as :meth:`_query.Query.from_self`.
-
- .. change:: 3548
- :tags: bug, firebird
- :tickets: 3548
-
- Ported the fix for Oracle quoted-lowercase names to Firebird, so that
- a table name that is quoted as lower case can be reflected properly
- including when the table name comes from the get_table_names()
- inspection function.
-
-.. changelog::
- :version: 1.1.4
- :released: November 15, 2016
-
- .. change:: 3842
- :tags: bug, sql
- :tickets: 3842
-
- Fixed bug where newly added warning for primary key on insert w/o
- autoincrement setting (see :ticket:`3216`) would fail to emit
- correctly when invoked upon a lower-case :func:`.table` construct.
-
- .. change:: 3852
- :tags: bug, orm
- :tickets: 3852
-
- Fixed regression in collections due to :ticket:`3457` whereby
- deserialize during pickle or deepcopy would fail to establish all
- attributes of an ORM collection, causing further mutation operations to
- fail.
-
- .. change:: default_schema
- :tags: bug, engine
-
- Removed long-broken "default_schema_name()" method from
- :class:`_engine.Connection`. This method was left over from a very old
- version and was non-working (e.g. would raise). Pull request
- courtesy Benjamin Dopplinger.
-
- .. change:: pragma
- :tags: bug, sqlite
-
- Added quotes to the PRAGMA directives in the pysqlcipher dialect
- to support additional cipher arguments appropriately. Pull request
- courtesy Kevin Jurczyk.
-
- .. change:: 3846
- :tags: bug, postgresql
- :tickets: 3846, 3807
-
- Fixed regression caused by the fix in :ticket:`3807` (version 1.1.0)
- where we ensured that the tablename was qualified in the WHERE clause
- of the DO UPDATE portion of PostgreSQL's ON CONFLICT, however you
- *cannot* put the table name in the WHERE clause in the actual ON
- CONFLICT itself. This was an incorrect assumption, so that portion
- of the change in :ticket:`3807` is rolled back.
-
- .. change:: 3845
- :tags: bug, orm
- :tickets: 3845
-
- Fixed long-standing bug where the "noload" relationship loading
- strategy would cause backrefs and/or back_populates options to be
- ignored.
-
- .. change:: sscursor_mysql
- :tags: feature, mysql
-
- Added support for server side cursors to the mysqlclient and
- pymysql dialects. This feature is available via the
- :paramref:`.Connection.execution_options.stream_results` flag as well
- as the ``server_side_cursors=True`` dialect argument in the
- same way that it has been for psycopg2 on PostgreSQL. Pull request
- courtesy Roman Podoliaka.
-
- .. change::
- :tags: bug, mysql
- :tickets: 3841
-
- MySQL's native ENUM type supports any non-valid value being sent, and
- in response will return a blank string. A hardcoded rule to check for
- "is returning the blank string" has been added to the MySQL
- implementation for ENUM so that this blank string is returned to the
- application rather than being rejected as a non-valid value. Note that
- if your MySQL enum is linking values to objects, you still get the
- blank string back.
-
- .. change::
- :tags: bug, sqlite, py3k
-
- Added an optional import for the pysqlcipher3 DBAPI when using the
- pysqlcipher dialect. This package will attempt to be imported
- if the Python-2 only pysqlcipher DBAPI is non-present.
- Pull request courtesy Kevin Jurczyk.
-
-.. changelog::
- :version: 1.1.3
- :released: October 27, 2016
-
- .. change::
- :tags: bug, orm
- :tickets: 3839
-
- Fixed regression caused by :ticket:`2677` whereby calling
- :meth:`.Session.delete` on an object that was already flushed as
- deleted in that session would fail to set up the object in the
- identity map (or reject the object), causing flush errors as the
- object were in a state not accommodated by the unit of work.
- The pre-1.1 behavior in this case has been restored, which is that
- the object is put back into the identity map so that the DELETE
- statement will be attempted again, which emits a warning that the number
- of expected rows was not matched (unless the row were restored outside
- of the session).
-
- .. change::
- :tags: bug, postgresql
- :tickets: 3835
-
- PostgreSQL table reflection will ensure that the
- :paramref:`_schema.Column.autoincrement` flag is set to False when reflecting
- a primary key column that is not of an :class:`.Integer` datatype,
- even if the default is related to an integer-generating sequence.
- This can happen if a column is created as SERIAL and the datatype
- is changed. The autoincrement flag can only be True if the datatype
- is of integer affinity in the 1.1 series.
-
- .. change::
- :tags: bug, orm
- :tickets: 3836
-
- Fixed regression where some :class:`_query.Query` methods like
- :meth:`_query.Query.update` and others would fail if the :class:`_query.Query`
- were against a series of mapped columns, rather than the mapped
- entity as a whole.
-
- .. change::
- :tags: bug, sql
- :tickets: 3833
-
- Fixed bug involving new value translation and validation feature
- in :class:`.Enum` whereby using the enum object in a string
- concatenation would maintain the :class:`.Enum` type as the type
- of the expression overall, producing missing lookups. A string
- concatenation against an :class:`.Enum`-typed column now uses
- :class:`.String` as the datatype of the expression itself.
-
- .. change::
- :tags: bug, sql
- :tickets: 3832
-
- Fixed regression which occurred as a side effect of :ticket:`2919`,
- which in the less typical case of a user-defined
- :class:`.TypeDecorator` that was also itself an instance of
- :class:`.SchemaType` (rather than the implementation being such)
- would cause the column attachment events to be skipped for the
- type itself.
-
-
-.. changelog::
- :version: 1.1.2
- :released: October 17, 2016
-
- .. change::
- :tags: bug, sql
- :tickets: 3823
-
- Fixed a regression caused by a newly added function that performs the
- "wrap callable" function of sql :class:`.DefaultGenerator` objects,
- an attribute error raised for ``__module__`` when the default callable
- was a ``functools.partial`` or other object that doesn't have a
- ``__module__`` attribute.
-
- .. change::
- :tags: bug, orm
- :tickets: 3824
-
- Fixed bug involving the rule to disable a joined collection eager
- loader on the other side of a many-to-one lazy loader, first added
- in :ticket:`1495`, where the rule would fail if the parent object
- had some other lazyloader-bound query options associated with it.
-
- .. change::
- :tags: bug, orm
- :tickets: 3822
-
- Fixed self-referential entity, deferred column loading issue in a
- similar style as that of :ticket:`3431`, :ticket:`3811` where an entity
- is present in multiple positions within the row due to self-referential
- eager loading; when the deferred loader only applies to one of the
- paths, the "present" column loader will now override the deferred non-
- load for that entity regardless of row ordering.
-
- .. change::
- :tags: bug, sql, postgresql
- :tickets: 3827
-
- Fixed regression in :class:`.Enum` type where event handlers were not
- transferred in the case of the type object being copied, due to a
- conflicting copy() method added as part of :ticket:`3250`. This copy
- occurs normally in situations when the column is copied, such as
- in tometadata() or when using declarative mixins with columns. The
- event handler not being present would impact the constraint being
- created for a non-native enumerated type, but more critically the
- ENUM object on the PostgreSQL backend.
-
-
- .. change::
- :tags: bug, postgresql, sql
- :tickets: 3828
-
- Changed the naming convention used when generating bound parameters
- for a multi-VALUES insert statement, so that the numbered parameter
- names don't conflict with the anonymized parameters of a WHERE clause,
- as is now common in a PostgreSQL ON CONFLICT construct.
-
-.. changelog::
- :version: 1.1.1
- :released: October 7, 2016
-
- .. change::
- :tags: bug, mssql
- :tickets: 3820
-
- The "SELECT SERVERPROPERTY"
- query added in :ticket:`3810` and :ticket:`3814` is failing on unknown
- combinations of Pyodbc and SQL Server. While failure of this function
- was anticipated, the exception catch was not broad enough so it now
- catches all forms of pyodbc.Error.
-
- .. change::
- :tags: bug, core
- :tickets: 3216
-
- Changed the CompileError raised when various primary key missing
- situations are detected to a warning. The statement is again
- passed to the database where it will fail and the DBAPI error (usually
- IntegrityError) raises as usual.
-
- .. seealso::
-
- :ref:`change_3216`
-
-.. changelog::
- :version: 1.1.0
- :released: October 5, 2016
-
- .. change::
- :tags: bug, sql
- :tickets: 3805
-
- Execution options can now be propagated from within a
- statement at compile time to the outermost statement, so that
- if an embedded element wants to set "autocommit" to be True for example,
- it can propagate this to the enclosing statement. Currently, this
- feature is enabled for a DML-oriented CTE embedded inside of a SELECT
- statement, e.g. INSERT/UPDATE/DELETE inside of SELECT.
-
- .. change::
- :tags: bug, orm
- :tickets: 3802
-
- ORM attributes can now be assigned any object that is has a
- ``__clause_element__()`` attribute, which will result in inline
- SQL the way any :class:`_expression.ClauseElement` class does. This covers other
- mapped attributes not otherwise transformed by further expression
- constructs.
-
- .. change::
- :tags: feature, orm
- :tickets: 3812
-
- Enhanced the new "raise" lazy loader strategy to also include a
- "raise_on_sql" variant, available both via :paramref:`.orm.relationship.lazy`
- as well as :func:`_orm.raiseload`. This variant only raises if the
- lazy load would actually emit SQL, vs. raising if the lazy loader
- mechanism is invoked at all.
-
- .. change::
- :tags: bug, postgresql
- :tickets: 3813
-
- An adjustment to ON CONFLICT such that the "inserted_primary_key"
- logic is able to accommodate the case where there's no INSERT or
- UPDATE and there's no net change. The value comes out as None
- in this case, rather than failing on an exception.
-
- .. change::
- :tags: bug, orm
- :tickets: 3811
-
- Made an adjustment to the bug fix first introduced in [ticket:3431]
- that involves an object appearing in multiple contexts in a single
- result set, such that an eager loader that would set the related
- object value to be None will still fire off, thus satisfying the
- load of that attribute. Previously, the adjustment only honored
- a non-None value arriving for an eagerly loaded attribute in a
- secondary row.
-
- .. change::
- :tags: bug, orm
- :tickets: 3808
-
- Fixed bug in new :meth:`.SessionEvents.persistent_to_deleted` event
- where the target object could be garbage collected before the event
- is fired off.
-
- .. change::
- :tags: bug, sql
- :tickets: 3809
-
- A string sent as a column default via the
- :paramref:`_schema.Column.server_default` parameter is now escaped for quotes.
-
- .. seealso::
-
- :ref:`change_3809`
-
- .. change::
- :tags: bug, postgresql
- :tickets: 3807
-
- Fixed issue in new PG "on conflict" construct where columns including
- those of the "excluded" namespace would not be table-qualified
- in the WHERE clauses in the statement.
-
- .. change::
- :tags: bug, sql, postgresql
- :tickets: 3806
-
- Added compiler-level flags used by PostgreSQL to place additional
- parenthesis than would normally be generated by precedence rules
- around operations involving JSON, HSTORE indexing operators as well as
- within their operands since it has been observed that PostgreSQL's
- precedence rules for at least the HSTORE indexing operator is not
- consistent between 9.4 and 9.5.
-
- .. change::
- :tags: bug, sql, mysql
- :tickets: 3803
-
- The ``BaseException`` exception class is now intercepted by the
- exception-handling routines of :class:`_engine.Connection`, and includes
- handling by the :meth:`_events.ConnectionEvents.handle_error`
- event. The :class:`_engine.Connection` is now **invalidated** by default in
- the case of a system level exception that is not a subclass of
- ``Exception``, including ``KeyboardInterrupt`` and the greenlet
- ``GreenletExit`` class, to prevent further operations from occurring
- upon a database connection that is in an unknown and possibly
- corrupted state. The MySQL drivers are most targeted by this change
- however the change is across all DBAPIs.
-
- .. seealso::
-
- :ref:`change_3803`
-
- .. change::
- :tags: bug, sql
- :tickets: 3799
-
- The "eq" and "ne" operators are no longer part of the list of
- "associative" operators, while they remain considered to be
- "commutative". This allows an expression like ``(x == y) == z``
- to be maintained at the SQL level with parenthesis. Pull request
- courtesy John Passaro.
-
- .. change::
- :tags: bug, orm
- :tickets: 3767
-
- The primaryjoin of a :func:`_orm.relationship` construct can now include
- a :func:`.bindparam` object that includes a callable function to
- generate values. Previously, the lazy loader strategy would
- be incompatible with this use, and additionally would fail to correctly
- detect if the "use_get" criteria should be used if the primary key
- were involved with the bound parameter.
-
- .. change::
- :tags: bug, orm
- :tickets: 3801
-
- An UPDATE emitted from the ORM flush process can now accommodate a
- SQL expression element for a column within the primary key of an
- object, if the target database supports RETURNING in order to provide
- the new value, or if the PK value is set "to itself" for the purposes
- of bumping some other trigger / onupdate on the column.
-
- .. change::
- :tags: bug, orm
- :tickets: 3788
-
- Fixed bug where the "simple many-to-one" condition that allows lazy
- loading to use get() from identity map would fail to be invoked if the
- primaryjoin of the relationship had multiple clauses separated by AND
- which were not in the same order as that of the primary key columns
- being compared in each clause. This ordering
- difference occurs for a composite foreign key where the table-bound
- columns on the referencing side were not in the same order in the .c
- collection as the primary key columns on the referenced side....which
- in turn occurs a lot if one is using declarative mixins and/or
- declared_attr to set up columns.
-
- .. change::
- :tags: bug, sql
- :tickets: 3789
-
- Stringify of expression with unnamed :class:`_schema.Column` objects, as
- occurs in lots of situations including ORM error reporting,
- will now render the name in string context as "<name unknown>"
- rather than raising a compile error.
-
- .. change::
- :tags: bug, sql
- :tickets: 3786
-
- Raise a more descriptive exception / message when ClauseElement
- or non-SQLAlchemy objects that are not "executable" are erroneously
- passed to ``.execute()``; a new exception ObjectNotExecutableError
- is raised consistently in all cases.
-
- .. change::
- :tags: bug, orm
- :tickets: 3776
-
- An exception is raised when two ``@validates`` decorators on a mapping
- make use of the same name. Only one validator of a certain name
- at a time is supported, there's no mechanism to chain these together,
- as the order of the validators at the level of function decorator
- can't be made deterministic.
-
- .. seealso::
-
- :ref:`change_3776`
-
- .. change::
- :tags: bug, orm
-
- Mapper errors raised during :func:`.configure_mappers` now explicitly
- include the name of the originating mapper in the exception message
- to help in those situations where the wrapped exception does not
- itself include the source mapper. Pull request courtesy
- John Perkins.
-
- .. change::
- :tags: bug, mysql
- :tickets: 3766
-
- Fixed bug where the "literal_binds" flag would not be propagated
- to a CAST expression under MySQL.
-
- .. change::
- :tags: bug, sql, postgresql, mysql
- :tickets: 3765
-
- Fixed regression in JSON datatypes where the "literal processor" for
- a JSON index value would not be invoked. The native String and Integer
- datatypes are now called upon from within the JSONIndexType
- and JSONPathType. This is applied to the generic, PostgreSQL, and
- MySQL JSON types and also has a dependency on :ticket:`3766`.
-
- .. change::
- :tags: change, orm
-
- Passing False to :meth:`_query.Query.order_by` in order to cancel
- all order by's is deprecated; there is no longer any difference
- between calling this method with False or with None.
-
- .. change::
- :tags: feature, orm
-
- The :meth:`_query.Query.group_by` method now resets the group by collection
- if an argument of ``None`` is passed, in the same way that
- :meth:`_query.Query.order_by` has worked for a long time. Pull request
- courtesy Iuri Diniz.
-
- .. change::
- :tags: bug, sql
- :tickets: 3763
-
- Fixed bug where :class:`.Index` would fail to extract columns from
- compound SQL expressions if those SQL expressions were wrapped inside
- of an ORM-style ``__clause_element__()`` construct. This bug
- exists in 1.0.x as well, however in 1.1 is more noticeable as
- hybrid_property @expression now returns a wrapped element.
-
- .. change::
- :tags: change, orm, declarative
-
- Constructing a declarative base class that inherits from another class
- will also inherit its docstring. This means
- :func:`~.ext.declarative.as_declarative` acts more like a normal class
- decorator.
-
-.. changelog::
- :version: 1.1.0b3
- :released: July 26, 2016
-
- .. change::
- :tags: change, orm
- :tickets: 3749
-
- Removed a warning that dates back to 0.4 which emits when a same-named
- relationship is placed on two mappers that inherits via joined or
- single table inheritance. The warning does not apply to the
- current unit of work implementation.
-
- .. seealso::
-
- :ref:`change_3749`
-
-
- .. change::
- :tags: bug, sql
- :tickets: 3745
-
- Fixed bug in new CTE feature for update/insert/delete stated
- as a CTE inside of an enclosing statement (typically SELECT) whereby
- oninsert and onupdate values weren't called upon for the embedded
- statement.
-
- .. change::
- :tags: bug, sql
- :tickets: 3744
-
- Fixed bug in new CTE feature for update/insert/delete whereby
- an anonymous (e.g. no name passed) :class:`_expression.CTE` construct around
- the statement would fail.
-
- .. change::
- :tags: bug, ext
-
- sqlalchemy.ext.indexable will intercept IndexError as well
- as KeyError when raising as AttributeError.
-
- .. change::
- :tags: feature, ext
-
- Added a "default" parameter to the new sqlalchemy.ext.indexable
- extension.
-
-.. changelog::
- :version: 1.1.0b2
- :released: July 1, 2016
-
- .. change::
- :tags: bug, ext, postgresql
- :tickets: 3732
-
- Made a slight behavioral change in the ``sqlalchemy.ext.compiler``
- extension, whereby the existing compilation schemes for an established
- construct would be removed if that construct itself didn't already
- have its own dedicated ``__visit_name__``. This was a
- rare occurrence in 1.0, however in 1.1 :class:`_postgresql.ARRAY`
- subclasses :class:`_types.ARRAY` and has this behavior.
- As a result, setting up a compilation handler for another dialect
- such as SQLite would render the main :class:`_postgresql.ARRAY`
- object no longer compilable.
-
- .. change::
- :tags: bug, sql
- :tickets: 3730
-
- The processing performed by the :class:`.Boolean` datatype for backends
- that only feature integer types has been made consistent between the
- pure Python and C-extension versions, in that the C-extension version
- will accept any integer value from the database as a boolean, not just
- zero and one; additionally, non-boolean integer values being sent to
- the database are coerced to exactly zero or one, instead of being
- passed as the original integer value.
-
- .. seealso::
-
- :ref:`change_3730`
-
- .. change::
- :tags: bug, sql
- :tickets: 3725
-
- Rolled back the validation rules a bit in :class:`.Enum` to allow
- unknown string values to pass through, unless the flag
- ``validate_string=True`` is passed to the Enum; any other kind of object is
- still of course rejected. While the immediate use
- is to allow comparisons to enums with LIKE, the fact that this
- use exists indicates there may be more unknown-string-comparison use
- cases than we expected, which hints that perhaps there are some
- unknown string-INSERT cases too.
-
- .. change::
- :tags: bug, mysql
- :tickets: 3726
-
- Dialed back the "order the primary key columns per auto-increment"
- described in :ref:`change_mysql_3216` a bit, so that if the
- :class:`.PrimaryKeyConstraint` is explicitly defined, the order
- of columns is maintained exactly, allowing control of this behavior
- when necessary.
-
-.. changelog::
- :version: 1.1.0b1
- :released: June 16, 2016
-
- .. change::
- :tags: feature, sql
- :tickets: 3718
-
- Added TABLESAMPLE support via the new :meth:`_expression.FromClause.tablesample`
- method and standalone function. Pull request courtesy Ilja Everilä.
-
- .. seealso::
-
- :ref:`change_3718`
-
- .. change::
- :tags: feature, orm, ext
-
- A new ORM extension :ref:`indexable_toplevel` is added, which allows
- construction of Python attributes which refer to specific elements
- of "indexed" structures such as arrays and JSON fields. Pull request
- courtesy Jeong YunWon.
-
- .. seealso::
-
- :ref:`feature_indexable`
-
- .. change::
- :tags: bug, sql
- :tickets: 3724
-
- :meth:`_expression.FromClause.count` is deprecated. This function makes use of
- an arbitrary column in the table and is not reliable; for Core use,
- ``func.count()`` should be preferred.
-
- .. change::
- :tags: feature, postgresql
- :tickets: 3529
-
- Added support for PostgreSQL's INSERT..ON CONFLICT using a new
- PostgreSQL-specific :class:`.postgresql.dml.Insert` object.
- Pull request and extensive efforts here by Robin Thomas.
-
- .. seealso::
-
- :ref:`change_3529`
-
- .. change::
- :tags: feature, postgresql
-
- The DDL for DROP INDEX will emit "CONCURRENTLY" if the
- ``postgresql_concurrently`` flag is set upon the
- :class:`.Index` and if the database in use is detected as
- PostgreSQL version 9.2 or greater. For CREATE INDEX, database
- version detection is also added which will omit the clause if
- PG version is less than 8.2. Pull request courtesy Iuri de Silvio.
-
- .. change::
- :tags: bug, orm
- :tickets: 3708
-
- Fixed an issue where a many-to-one change of an object from one
- parent to another could work inconsistently when combined with
- an un-flushed modification of the foreign key attribute. The attribute
- move now considers the database-committed value of the foreign key
- in order to locate the "previous" parent of the object being
- moved. This allows events to fire off correctly including
- backref events. Previously, these events would not always fire.
- Applications which may have relied on the previously broken
- behavior may be affected.
-
- .. seealso::
-
- :ref:`change_3708`
-
- .. change::
- :tags: feature, sql
- :tickets: 3049
-
- Added support for ranges in window functions, using the
- :paramref:`.expression.over.range_` and
- :paramref:`.expression.over.rows` parameters.
-
- .. seealso::
-
- :ref:`change_3049`
-
- .. change::
- :tags: feature, orm
-
- Added new flag :paramref:`.Session.bulk_insert_mappings.render_nulls`
- which allows an ORM bulk INSERT to occur with NULL values rendered;
- this bypasses server side defaults, however allows all statements
- to be formed with the same set of columns, allowing them to be
- batched. Pull request courtesy Tobias Sauerwein.
-
- .. change::
- :tags: feature, postgresql
- :tickets: 3588
-
- Added new parameter :paramref:`.PGInspector.get_view_names.include`,
- allowing specification for what kinds of views should be returned.
- Currently "plain" and "materialized" views are included. Pull
- request courtesy Sebastian Bank.
-
- .. change::
- :tags: feature, mssql
-
- The ``mssql_clustered`` flag available on :class:`.UniqueConstraint`,
- :class:`.PrimaryKeyConstraint`, :class:`.Index` now defaults to
- ``None``, and can be set to False which will render the NONCLUSTERED
- keyword in particular for a primary key, allowing a different index to
- be used as "clustered". Pull request courtesy Saulius Žemaitaitis.
-
- .. change::
- :tags: feature, orm
- :tickets: 1311
-
- Added new event :meth:`.AttributeEvents.init_scalar`, as well
- as a new example suite illustrating its use. This event can be used
- to provide a Core-generated default value to a Python-side attribute
- before the object is persisted.
-
- .. seealso::
-
- :ref:`change_1311`
-
- .. change::
- :tags: feature, postgresql
- :tickets: 3720
-
- Added ``postgresql_tablespace`` as an argument to :class:`.Index`
- to allow specification of TABLESPACE for an index in PostgreSQL.
- Complements the same-named parameter on :class:`_schema.Table`. Pull
- request courtesy Benjamin Bertrand.
-
- .. change::
- :tags: orm, feature
-
- Added :paramref:`.AutomapBase.prepare.schema` to the
- :meth:`.AutomapBase.prepare` method, to indicate which schema
- tables should be reflected from if not the default schema.
- Pull request courtesy Josh Marlow.
-
- .. change::
- :tags: feature, sqlite
-
- The SQLite dialect now reflects ON UPDATE and ON DELETE phrases
- within foreign key constraints. Pull request courtesy
- Michal Petrucha.
-
- .. change::
- :tags: bug, mssql
-
- Adjustments to the mxODBC dialect to make use of the ``BinaryNull``
- symbol when appropriate in conjunction with the ``VARBINARY``
- data type. Pull request courtesy Sheila Allen.
-
- .. change::
- :tags: feature, sql
-
- Implemented reflection of CHECK constraints for SQLite and PostgreSQL.
- This is available via the new inspector method
- :meth:`_reflection.Inspector.get_check_constraints` as well as when reflecting
- :class:`_schema.Table` objects in the form of :class:`.CheckConstraint`
- objects present in the constraints collection. Pull request courtesy
- Alex Grönholm.
-
- .. change::
- :tags: feature, postgresql
-
- Added new parameter
- :paramref:`.GenerativeSelect.with_for_update.key_share`, which
- will render the ``FOR NO KEY UPDATE`` version of ``FOR UPDATE``
- and ``FOR KEY SHARE`` instead of ``FOR SHARE``
- on the PostgreSQL backend. Pull request courtesy Sergey Skopin.
-
- .. change::
- :tags: feature, postgresql, oracle
-
- Added new parameter
- :paramref:`.GenerativeSelect.with_for_update.skip_locked`, which
- will render the ``SKIP LOCKED`` phrase for a ``FOR UPDATE`` or
- ``FOR SHARE`` lock on the PostgreSQL and Oracle backends. Pull
- request courtesy Jack Zhou.
-
- .. change::
- :tags: change, orm
- :tickets: 3394
-
- The :paramref:`_orm.Mapper.order_by` parameter is deprecated.
- This is an old parameter no longer relevant to how SQLAlchemy
- works, once the Query object was introduced. By deprecating it
- we establish that we aren't supporting non-working use cases
- and that we encourage applications to move off of the use of this
- parameter.
-
- .. seealso::
-
- :ref:`change_3394`
-
- .. change::
- :tags: feature, postgresql
-
- Added a new dialect for the PyGreSQL PostgreSQL dialect. Thanks
- to Christoph Zwerschke and Kaolin Imago Fire for their efforts.
-
- .. change::
- :tags: bug, ext
- :tickets: 3653
-
- The docstring specified on a hybrid property or method is now honored
- at the class level, allowing it to work with tools like Sphinx
- autodoc. The mechanics here necessarily involve some wrapping of
- expressions to occur for hybrid properties, which may cause them
- to appear differently using introspection.
-
- .. seealso::
-
- :ref:`change_3653`
-
- .. change::
- :tags: feature, sql
-
- New :meth:`.ColumnOperators.is_distinct_from` and
- :meth:`.ColumnOperators.isnot_distinct_from` operators; pull request
- courtesy Sebastian Bank.
-
- .. seealso::
-
- :ref:`change_is_distinct_from`
-
- .. change::
- :tags: bug, orm
- :tickets: 3488
-
- Fixed bug where deferred columns would inadvertently be set up
- for database load on the next object-wide unexpire, when the object
- were merged into the session with ``session.merge(obj, load=False)``.
-
- .. change::
- :tags: feature, sql
-
- Added a hook in :meth:`.DDLCompiler.visit_create_table` called
- :meth:`.DDLCompiler.create_table_suffix`, allowing custom dialects
- to add keywords after the "CREATE TABLE" clause. Pull request
- courtesy Mark Sandan.
-
- .. change::
- :tags: feature, sql
-
- Negative integer indexes are now accommodated by rows
- returned from a :class:`_engine.ResultProxy`. Pull request courtesy
- Emanuele Gaifas.
-
- .. seealso::
-
- :ref:`change_gh_231`
-
- .. change::
- :tags: feature, sqlite
- :tickets: 3629
-
- The SQLite dialect now reflects the names of primary key constraints.
- Pull request courtesy Diana Clarke.
-
- .. seealso::
-
- :ref:`change_3629`
-
- .. change::
- :tags: feature, sql
- :tickets: 2857
-
- Added :meth:`_expression.Select.lateral` and related constructs to allow
- for the SQL standard LATERAL keyword, currently only supported
- by PostgreSQL.
-
- .. seealso::
-
- :ref:`change_2857`
-
- .. change::
- :tags: feature, sql
- :tickets: 1957
-
- Added support for rendering "FULL OUTER JOIN" to both Core and ORM.
- Pull request courtesy Stefan Urbanek.
-
- .. seealso::
-
- :ref:`change_1957`
-
- .. change::
- :tags: feature, engine
-
- Added connection pool events :meth:`ConnectionEvents.close`,
- :meth:`_events.ConnectionEvents.detach`,
- :meth:`_events.ConnectionEvents.close_detached`.
-
- .. change::
- :tags: bug, orm, mysql
- :tickets: 3680
-
- Further continuing on the common MySQL exception case of
- a savepoint being cancelled first covered in :ticket:`2696`,
- the failure mode in which the :class:`.Session` is placed when a
- SAVEPOINT vanishes before rollback has been improved to allow the
- :class:`.Session` to still function outside of that savepoint.
- It is assumed that the savepoint operation failed and was cancelled.
-
- .. seealso::
-
- :ref:`change_3680`
-
- .. change::
- :tags: feature, mssql
- :tickets: 3534
-
- Added basic isolation level support to the SQL Server dialects
- via :paramref:`_sa.create_engine.isolation_level` and
- :paramref:`.Connection.execution_options.isolation_level`
- parameters.
-
- .. seealso::
-
- :ref:`change_3534`
-
- .. change::
- :tags: feature, mysql
- :tickets: 3332
-
- Added support for "autocommit" on MySQL drivers, via the
- AUTOCOMMIT isolation level setting. Pull request courtesy
- Roman Podoliaka.
-
- .. seealso::
-
- :ref:`change_3332`
-
- .. change::
- :tags: bug, orm
- :tickets: 3677
-
- Fixed bug where a newly inserted instance that is rolled back
- would still potentially cause persistence conflicts on the next
- transaction, because the instance would not be checked that it
- was expired. This fix will resolve a large class of cases that
- erroneously cause the "New instance with identity X conflicts with
- persistent instance Y" error.
-
- .. seealso::
-
- :ref:`change_3677`
-
- .. change::
- :tags: bug, orm
- :tickets: 3662
-
- An improvement to the workings of :meth:`_query.Query.correlate` such
- that when a "polymorphic" entity is used which represents a straight
- join of several tables, the statement will ensure that all the
- tables within the join are part of what's correlating.
-
- .. seealso::
-
- :ref:`change_3662`
-
- .. change::
- :tags: bug, orm
- :tickets: 3431
-
- Fixed bug which would cause an eagerly loaded many-to-one attribute
- to not be loaded, if the joined eager load were from a row where the
- same entity were present multiple times, some calling for the attribute
- to be eagerly loaded and others not. The logic here is revised to
- take in the attribute even though a different loader path has
- handled the parent entity already.
-
- .. seealso::
-
- :ref:`change_3431`
-
- .. change::
- :tags: feature, engine
- :tickets: 2837
-
- All string formatting of bound parameter sets and result rows for
- logging, exception, and ``repr()`` purposes now truncate very large
- scalar values within each collection, including an
- "N characters truncated"
- notation, similar to how the display for large multiple-parameter sets
- are themselves truncated.
-
-
- .. seealso::
-
- :ref:`change_2837`
-
- .. change::
- :tags: feature, ext
- :tickets: 3297
-
- Added :class:`.MutableSet` and :class:`.MutableList` helper classes
- to the :ref:`mutable_toplevel` extension. Pull request courtesy
- Jeong YunWon.
-
- .. change::
- :tags: feature, sql
- :tickets: 2551
-
- CTE functionality has been expanded to support all DML, allowing
- INSERT, UPDATE, and DELETE statements to both specify their own
- WITH clause, as well as for these statements themselves to be
- CTE expressions when they include a RETURNING clause.
-
- .. seealso::
-
- :ref:`change_2551`
-
- .. change::
- :tags: bug, orm
- :tickets: 3641
-
- A refinement to the logic which adds columns to the resulting SQL when
- :meth:`_query.Query.distinct` is combined with :meth:`_query.Query.order_by` such
- that columns which are already present will not be added
- a second time, even if they are labeled with a different name.
- Regardless of this change, the extra columns added to the SQL have
- never been returned in the final result, so this change only impacts
- the string form of the statement as well as its behavior when used in
- a Core execution context. Additionally, columns are no longer added
- when the DISTINCT ON format is used, provided the query is not
- wrapped inside a subquery due to joined eager loading.
-
- .. seealso::
-
- :ref:`change_3641`
-
- .. change::
- :tags: feature, sql
- :tickets: 3292, 3095
-
- Added support for PEP-435-style enumerated classes, namely
- Python 3's ``enum.Enum`` class but also including compatible
- enumeration libraries, to the :class:`_types.Enum` datatype.
- The :class:`_types.Enum` datatype now also performs in-Python validation
- of incoming values, and adds an option to forego creating the
- CHECK constraint :paramref:`.Enum.create_constraint`.
- Pull request courtesy Alex Grönholm.
-
- .. seealso::
-
- :ref:`change_3292`
-
- :ref:`change_3095`
-
- .. change::
- :tags: change, postgresql
-
- The ``sqlalchemy.dialects.postgres`` module, long deprecated, is
- removed; this has emitted a warning for many years and projects
- should be calling upon ``sqlalchemy.dialects.postgresql``.
- Engine URLs of the form ``postgres://`` will still continue to function,
- however.
-
- .. change::
- :tags: bug, sqlite
- :tickets: 3634
-
- The workaround for right-nested joins on SQLite, where they are rewritten
- as subqueries in order to work around SQLite's lack of support for this
- syntax, is lifted when SQLite version 3.7.16 or greater is detected.
-
- .. seealso::
-
- :ref:`change_3634`
-
- .. change::
- :tags: bug, sqlite
- :tickets: 3633
-
- The workaround for SQLite's unexpected delivery of column names as
- ``tablename.columnname`` for some kinds of queries is now disabled
- when SQLite version 3.10.0 or greater is detected.
-
- .. seealso::
-
- :ref:`change_3633`
-
- .. change::
- :tags: feature, orm
- :tickets: 2349
-
- Added new parameter :paramref:`.orm.mapper.passive_deletes` to
- available mapper options. This allows a DELETE to proceed
- for a joined-table inheritance mapping against the base table only,
- while allowing for ON DELETE CASCADE to handle deleting the row
- from the subclass tables.
-
- .. seealso::
-
- :ref:`change_2349`
-
-
- .. change::
- :tags: bug, sybase
- :tickets: 2278
-
- The unsupported Sybase dialect now raises ``NotImplementedError``
- when attempting to compile a query that includes "offset"; Sybase
- has no straightforward "offset" feature.
-
- .. change::
- :tags: feature, orm
- :tickets: 3631
-
- Calling str() on a core SQL construct has been made more "friendly",
- when the construct contains non-standard SQL elements such as
- RETURNING, array index operations, or dialect-specific or custom
- datatypes. A string is now returned in these cases rendering an
- approximation of the construct (typically the PostgreSQL-style
- version of it) rather than raising an error.
-
- .. seealso::
-
- :ref:`change_3631`
-
- .. change::
- :tags: bug, orm
- :tickets: 3630
-
- Fixed issue where two same-named relationships that refer to
- a base class and a concrete-inherited subclass would raise an error
- if those relationships were set up using "backref", while setting up the
- identical configuration using relationship() instead with the conflicting
- names would succeed, as is allowed in the case of a concrete mapping.
-
- .. seealso::
-
- :ref:`change_3630`
-
- .. change::
- :tags: feature, orm
- :tickets: 3081
-
- The ``str()`` call for :class:`_query.Query` will now take into account
- the :class:`_engine.Engine` to which the :class:`.Session` is bound, when
- generating the string form of the SQL, so that the actual SQL
- that would be emitted to the database is shown, if possible. Previously,
- only the engine associated with the :class:`_schema.MetaData` to which the
- mappings are associated would be used, if present. If
- no bind can be located either on the :class:`.Session` or on
- the :class:`_schema.MetaData` to which the mappings are associated, then
- the "default" dialect is used to render the SQL, as was the case
- previously.
-
- .. seealso::
-
- :ref:`change_3081`
-
- .. change::
- :tags: feature, sql
- :tickets: 3501
-
- A deep improvement to the recently added :meth:`_expression.TextClause.columns`
- method, and its interaction with result-row processing, now allows
- the columns passed to the method to be positionally matched with the
- result columns in the statement, rather than matching on name alone.
- The advantage to this includes that when linking a textual SQL statement
- to an ORM or Core table model, no system of labeling or de-duping of
- common column names needs to occur, which also means there's no need
- to worry about how label names match to ORM columns and so-forth. In
- addition, the :class:`_engine.ResultProxy` has been further enhanced to
- map column and string keys to a row with greater precision in some
- cases.
-
- .. seealso::
-
- :ref:`change_3501` - feature overview
-
- :ref:`behavior_change_3501` - backwards compatibility remarks
-
- .. change::
- :tags: feature, engine
- :tickets: 2685
-
- Multi-tenancy schema translation for :class:`_schema.Table` objects is added.
- This supports the use case of an application that uses the same set of
- :class:`_schema.Table` objects in many schemas, such as schema-per-user.
- A new execution option
- :paramref:`.Connection.execution_options.schema_translate_map` is
- added.
-
- .. seealso::
-
- :ref:`change_2685`
-
- .. change::
- :tags: feature, engine
- :tickets: 3536
-
- Added a new entrypoint system to the engine to allow "plugins" to
- be stated in the query string for a URL. Custom plugins can
- be written which will be given the chance up front to alter and/or
- consume the engine's URL and keyword arguments, and then at engine
- create time will be given the engine itself to allow additional
- modifications or event registration. Plugins are written as a
- subclass of :class:`.CreateEnginePlugin`; see that class for
- details.
-
- .. change::
- :tags: feature, mysql
- :tickets: 3547
-
- Added :class:`.mysql.JSON` for MySQL 5.7. The JSON type provides
- persistence of JSON values in MySQL as well as basic operator support
- of "getitem" and "getpath", making use of the ``JSON_EXTRACT``
- function in order to refer to individual paths in a JSON structure.
-
- .. seealso::
-
- :ref:`change_3547`
-
- .. change::
- :tags: feature, sql
- :tickets: 3619
-
- Added a new type to core :class:`_types.JSON`. This is the
- base of the PostgreSQL :class:`_postgresql.JSON` type as well as that
- of the new :class:`.mysql.JSON` type, so that a PG/MySQL-agnostic
- JSON column may be used. The type features basic index and path
- searching support.
-
- .. seealso::
-
- :ref:`change_3619`
-
- .. change::
- :tags: bug, sql
- :tickets: 3616
-
- Fixed an assertion that would raise somewhat inappropriately
- if a :class:`.Index` were associated with a :class:`_schema.Column` that
- is associated with a lower-case-t :class:`_expression.TableClause`; the
- association should be ignored for the purposes of associating
- the index with a :class:`_schema.Table`.
-
- .. change::
- :tags: bug, orm
- :tickets: 3601
-
- The :meth:`.Session.merge` method now tracks pending objects by
- primary key before emitting an INSERT, and merges distinct objects with
- duplicate primary keys together as they are encountered, which is
- essentially semi-deterministic at best. This behavior
- matches what happens already with persistent objects.
-
- .. seealso::
-
- :ref:`change_3601`
-
- .. change::
- :tags: bug, postgresql
- :tickets: 3587
-
- Added support for reflecting the source of materialized views
- to the PostgreSQL version of the :meth:`_reflection.Inspector.get_view_definition`
- method.
-
- .. change::
- :tags: bug, orm
- :tickets: 3582
-
- Fixed bug where the "single table inheritance" criteria would be
- added onto the end of a query in some inappropriate situations, such
- as when querying from an exists() of a single-inheritance subclass.
-
- .. seealso::
-
- :ref:`change_3582`
-
- .. change::
- :tags: enhancement, schema
-
- The default generation functions passed to :class:`_schema.Column` objects
- are now run through "update_wrapper", or an equivalent function
- if a callable non-function is passed, so that introspection tools
- preserve the name and docstring of the wrapped function. Pull
- request courtesy hsum.
-
- .. change::
- :tags: change, sql, mysql
- :tickets: 3216
-
- The system by which a :class:`_schema.Column` considers itself to be an
- "auto increment" column has been changed, such that autoincrement
- is no longer implicitly enabled for a :class:`_schema.Table` that has a
- composite primary key. In order to accommodate being able to enable
- autoincrement for a composite PK member column while at the same time
- maintaining SQLAlchemy's long standing behavior of enabling
- implicit autoincrement for a single integer primary key, a third
- state has been added to the :paramref:`_schema.Column.autoincrement` parameter
- ``"auto"``, which is now the default.
-
- .. seealso::
-
- :ref:`change_3216`
-
- :ref:`change_mysql_3216`
-
- .. change::
- :tags: change, mysql
- :tickets: 3216
-
- The MySQL dialect no longer generates an extra "KEY" directive when
- generating CREATE TABLE DDL for a table using InnoDB with a
- composite primary key with AUTO_INCREMENT on a column that isn't the
- first column; to overcome InnoDB's limitation here, the PRIMARY KEY
- constraint is now generated with the AUTO_INCREMENT column placed
- first in the list of columns.
-
- .. seealso::
-
- :ref:`change_mysql_3216`
-
- :ref:`change_3216`
-
- .. change::
- :tags: change, sqlite
-
- Added support to the SQLite dialect for the
- :meth:`_reflection.Inspector.get_schema_names` method to work with SQLite;
- pull request courtesy Brian Van Klaveren. Also repaired support
- for creation of indexes with schemas as well as reflection of
- foreign key constraints in schema-bound tables.
-
- .. seealso::
-
- :ref:`change_sqlite_schemas`
-
- .. change::
- :tags: change, mssql
- :tickets: 3434
-
- The ``legacy_schema_aliasing`` flag, introduced in version 1.0.5
- as part of :ticket:`3424` to allow disabling of the MSSQL dialect's
- attempts to create aliases for schema-qualified tables, now defaults
- to False; the old behavior is now disabled unless explicitly turned on.
-
- .. seealso::
-
- :ref:`change_3434`
-
- .. change::
- :tags: bug, orm
- :tickets: 3250
-
- Added a new type-level modifier :meth:`.TypeEngine.evaluates_none`
- which indicates to the ORM that a positive set of None should be
- persisted as the value NULL, instead of omitting the column from
- the INSERT statement. This feature is used both as part of the
- implementation for :ticket:`3514` as well as a standalone feature
- available on any type.
-
- .. seealso::
-
- :ref:`change_3250`
-
- .. change::
- :tags: bug, postgresql
- :tickets: 2729
-
- The use of a :class:`_postgresql.ARRAY` object that refers
- to a :class:`_types.Enum` or :class:`_postgresql.ENUM` subtype
- will now emit the expected "CREATE TYPE" and "DROP TYPE" DDL when
- the type is used within a "CREATE TABLE" or "DROP TABLE".
-
- .. seealso::
-
- :ref:`change_2729`
-
- .. change::
- :tags: bug, sql
- :tickets: 3531
-
- The :func:`.type_coerce` construct is now a fully fledged Core
- expression element which is late-evaluated at compile time. Previously,
- the function was only a conversion function which would handle different
- expression inputs by returning either a :class:`.Label` of a column-oriented
- expression or a copy of a given :class:`.BindParameter` object,
- which in particular prevented the operation from being logically
- maintained when an ORM-level expression transformation would convert
- a column to a bound parameter (e.g. for lazy loading).
-
- .. seealso::
-
- :ref:`change_3531`
-
- .. change::
- :tags: bug, orm
- :tickets: 3526
-
- Internal calls to "bookkeeping" functions within
- :meth:`.Session.bulk_save_objects` and related bulk methods have
- been scaled back to the extent that this functionality is not
- currently used, e.g. checks for column default values to be
- fetched after an INSERT or UPDATE statement.
-
- .. change::
- :tags: feature, orm
- :tickets: 2677
-
- The :class:`.SessionEvents` suite now includes events to allow
- unambiguous tracking of all object lifecycle state transitions
- in terms of the :class:`.Session` itself, e.g. pending,
- transient, persistent, detached. The state of the object
- within each event is also defined.
-
- .. seealso::
-
- :ref:`change_2677`
-
- .. change::
- :tags: feature, orm
- :tickets: 2677
-
- Added a new session lifecycle state :term:`deleted`. This new state
- represents an object that has been deleted from the :term:`persistent`
- state and will move to the :term:`detached` state once the transaction
- is committed. This resolves the long-standing issue that objects
- which were deleted existed in a gray area between persistent and
- detached. The :attr:`.InstanceState.persistent` accessor will
- **no longer** report on a deleted object as persistent; the
- :attr:`.InstanceState.deleted` accessor will instead be True for
- these objects, until they become detached.
-
- .. seealso::
-
- :ref:`change_2677`
-
- .. change::
- :tags: change, orm
- :tickets: 2677
-
- The :paramref:`.Session.weak_identity_map` parameter is deprecated.
- See the new recipe at :ref:`session_referencing_behavior` for
- an event-based approach to maintaining strong identity map behavior.
-
- .. seealso::
-
- :ref:`change_2677`
-
- .. change::
- :tags: bug, sql
- :tickets: 2919
-
- The :class:`.TypeDecorator` type extender will now work in conjunction
- with a :class:`.SchemaType` implementation, typically :class:`.Enum`
- or :class:`.Boolean` with regards to ensuring that the per-table
- events are propagated from the implementation type to the outer type.
- These events are used
- to ensure that the constraints or PostgreSQL types (e.g. ENUM)
- are correctly created (and possibly dropped) along with the parent
- table.
-
- .. seealso::
-
- :ref:`change_2919`
-
- .. change::
- :tags: feature, sql
- :tickets: 1370
-
- Added support for "set-aggregate" functions of the form
- ``<function> WITHIN GROUP (ORDER BY <criteria>)``, using the
- method :meth:`.FunctionElement.within_group`. A series of common
- set-aggregate functions with return types derived from the set have
- been added. This includes functions like :class:`.percentile_cont`,
- :class:`.dense_rank` and others.
-
- .. seealso::
-
- :ref:`change_3132`
-
- .. change::
- :tags: feature, sql, postgresql
- :tickets: 3132
-
- Added support for the SQL-standard function :class:`_functions.array_agg`,
- which automatically returns an :class:`_postgresql.ARRAY` of the correct type
- and supports index / slice operations, as well as
- :func:`_postgresql.array_agg`, which returns a :class:`_postgresql.ARRAY`
- with additional comparison features. As arrays are only
- supported on PostgreSQL at the moment, only actually works on
- PostgreSQL. Also added a new construct
- :class:`_postgresql.aggregate_order_by` in support of PG's
- "ORDER BY" extension.
-
- .. seealso::
-
- :ref:`change_3132`
-
- .. change::
- :tags: feature, sql
- :tickets: 3516
-
- Added a new type to core :class:`_types.ARRAY`. This is the
- base of the PostgreSQL :class:`_postgresql.ARRAY` type, and is now part of Core
- to begin supporting various SQL-standard array-supporting features
- including some functions and eventual support for native arrays
- on other databases that have an "array" concept, such as DB2 or Oracle.
- Additionally, new operators :func:`_expression.any_` and
- :func:`_expression.all_` have been added. These support not just
- array constructs on PostgreSQL, but also subqueries that are usable
- on MySQL (but sadly not on PostgreSQL).
-
- .. seealso::
-
- :ref:`change_3516`
-
- .. change::
- :tags: feature, orm
- :tickets: 3321
-
- Added new checks for the common error case of passing mapped classes
- or mapped instances into contexts where they are interpreted as
- SQL bound parameters; a new exception is raised for this.
-
- .. seealso::
-
- :ref:`change_3321`
-
- .. change::
- :tags: bug, postgresql
- :tickets: 3499
-
- The "hashable" flag on special datatypes such as :class:`_postgresql.ARRAY`,
- :class:`_postgresql.JSON` and :class:`_postgresql.HSTORE` is now
- set to False, which allows these types to be fetchable in ORM
- queries that include entities within the row.
-
- .. seealso::
-
- :ref:`change_3499`
-
- :ref:`change_3499_postgresql`
-
- .. change::
- :tags: bug, postgresql
- :tickets: 3487
-
- The PostgreSQL :class:`_postgresql.ARRAY` type now supports multidimensional
- indexed access, e.g. expressions such as ``somecol[5][6]`` without
- any need for explicit casts or type coercions, provided
- that the :paramref:`.postgresql.ARRAY.dimensions` parameter is set to the
- desired number of dimensions.
-
- .. seealso::
-
- :ref:`change_3503`
-
- .. change::
- :tags: bug, postgresql
- :tickets: 3503
-
- The return type for the :class:`_postgresql.JSON` and :class:`_postgresql.JSONB`
- when using indexed access has been fixed to work like PostgreSQL itself,
- and returns an expression that itself is of type :class:`_postgresql.JSON`
- or :class:`_postgresql.JSONB`. Previously, the accessor would return
- :class:`.NullType` which disallowed subsequent JSON-like operators to be
- used.
-
- .. seealso::
-
- :ref:`change_3503`
-
- .. change::
- :tags: bug, postgresql
- :tickets: 3503
-
- The :class:`_postgresql.JSON`, :class:`_postgresql.JSONB` and
- :class:`_postgresql.HSTORE` datatypes now allow full control over the
- return type from an indexed textual access operation, either ``column[someindex].astext``
- for a JSON type or ``column[someindex]`` for an HSTORE type,
- via the :paramref:`.postgresql.JSON.astext_type` and
- :paramref:`.postgresql.HSTORE.text_type` parameters.
-
- .. seealso::
-
- :ref:`change_3503`
-
-
- .. change::
- :tags: bug, postgresql
- :tickets: 3503
-
- The :attr:`.postgresql.JSON.Comparator.astext` modifier no longer
- calls upon :meth:`_expression.ColumnElement.cast` implicitly, as PG's JSON/JSONB
- types allow cross-casting between each other as well. Code that
- makes use of :meth:`_expression.ColumnElement.cast` on JSON indexed access,
- e.g. ``col[someindex].cast(Integer)``, will need to be changed
- to call :attr:`.postgresql.JSON.Comparator.astext` explicitly.
-
- .. seealso::
-
- :ref:`change_3503_cast`
-
-
- .. change::
- :tags: bug, orm, postgresql
- :tickets: 3514
-
- Additional fixes have been made regarding the value of ``None``
- in conjunction with the PostgreSQL :class:`_postgresql.JSON` type. When
- the :paramref:`_types.JSON.none_as_null` flag is left at its default
- value of ``False``, the ORM will now correctly insert the JSON
- "'null'" string into the column whenever the value on the ORM
- object is set to the value ``None`` or when the value ``None``
- is used with :meth:`.Session.bulk_insert_mappings`,
- **including** if the column has a default or server default on it.
-
- .. seealso::
-
- :ref:`change_3514`
-
- :ref:`change_3250`
-
- .. change::
- :tags: feature, postgresql
- :tickets: 3514
-
- Added a new constant :attr:`.postgresql.JSON.NULL`, indicating
- that the JSON NULL value should be used for a value
- regardless of other settings.
-
- .. seealso::
-
- :ref:`change_3514_jsonnull`
-
- .. change::
- :tags: bug, sql
- :tickets: 2528
-
- The behavior of the :func:`_expression.union` construct and related constructs
- such as :meth:`_query.Query.union` now handle the case where the embedded
- SELECT statements need to be parenthesized due to the fact that they
- include LIMIT, OFFSET and/or ORDER BY. These queries **do not work
- on SQLite**, and will fail on that backend as they did before, but
- should now work on all other backends.
-
- .. seealso::
-
- :ref:`change_2528`
-
- .. change::
- :tags: feature, orm
- :tickets: 3512
-
- Added new relationship loading strategy :func:`_orm.raiseload` (also
- accessible via ``lazy='raise'``). This strategy behaves almost like
- :func:`_orm.noload` but instead of returning ``None`` it raises an
- InvalidRequestError. Pull request courtesy Adrian Moennich.
-
- .. seealso::
-
- :ref:`change_3512`
-
- .. change::
- :tags: bug, mssql
- :tickets: 3504
-
- Fixed issue where the SQL Server dialect would reflect a string-
- or other variable-length column type with unbounded length
- by assigning the token ``"max"`` to the
- length attribute of the string. While using the ``"max"`` token
- explicitly is supported by the SQL Server dialect, it isn't part
- of the normal contract of the base string types, and instead the
- length should just be left as None. The dialect now assigns the
- length to None on reflection of the type so that the type behaves
- normally in other contexts.
-
- .. seealso::
-
- :ref:`change_3504`
+++ /dev/null
-=============
-1.2 Changelog
-=============
-
-.. changelog_imports::
-
- .. include:: changelog_11.rst
- :start-line: 5
-
-
- .. include:: changelog_10.rst
- :start-line: 5
-
-
-.. changelog::
- :version: 1.2.19
- :released: April 15, 2019
-
- .. change::
- :tags: bug, orm
- :tickets: 4507
-
- Fixed a regression in 1.2 due to the introduction of baked queries for
- relationship lazy loaders, where a race condition is created during the
- generation of the "lazy clause" which occurs within a memoized attribute. If
- two threads initialize the memoized attribute concurrently, the baked query
- could be generated with bind parameter keys that are then replaced with new
- keys by the next run, leading to a lazy load query that specifies the
- related criteria as ``None``. The fix establishes that the parameter names
- are fixed before the new clause and parameter objects are generated, so that
- the names are the same every time.
-
- .. change::
- :tags: bug, oracle
- :tickets: 4506
-
- Added support for reflection of the :class:`_types.NCHAR` datatype to the Oracle
- dialect, and added :class:`_types.NCHAR` to the list of types exported by the
- Oracle dialect.
-
-
- .. change::
- :tags: bug, examples
- :tickets: 4528
-
- Fixed bug in large_resultsets example case where a re-named "id" variable
- due to code reformatting caused the test to fail. Pull request courtesy
- Matt Schuchhardt.
-
- .. change::
- :tags: bug, mssql
- :tickets: 4536
- :versions: 1.3.1
-
- A commit() is emitted after an isolation level change to SNAPSHOT, as both
- pyodbc and pymssql open an implicit transaction which blocks subsequent SQL
- from being emitted in the current transaction.
-
- .. change::
- :tags: bug, engine
- :tickets: 4406
-
- Comparing two objects of :class:`.URL` using ``__eq__()`` did not take port
- number into consideration, two objects differing only by port number were
- considered equal. Port comparison is now added in ``__eq__()`` method of
- :class:`.URL`, objects differing by port number are now not equal.
- Additionally, ``__ne__()`` was not implemented for :class:`.URL` which
- caused unexpected result when ``!=`` was used in Python2, since there are no
- implied relationships among the comparison operators in Python2.
-
-.. changelog::
- :version: 1.2.18
- :released: February 15, 2019
-
- .. change::
- :tags: bug, orm
- :tickets: 4468
-
- Fixed a regression in 1.2 where a wildcard/load_only loader option would
- not work correctly against a loader path where of_type() were used to limit
- to a particular subclass. The fix only works for of_type() of a simple
- subclass so far, not a with_polymorphic entity which will be addressed in a
- separate issue; it is unlikely this latter case was working previously.
-
-
- .. change::
- :tags: bug, orm
- :tickets: 4489
-
- Fixed fairly simple but critical issue where the
- :meth:`.SessionEvents.pending_to_persistent` event would be invoked for
- objects not just when they move from pending to persistent, but when they
- were also already persistent and just being updated, thus causing the event
- to be invoked for all objects on every update.
-
- .. change::
- :tags: bug, sql
- :tickets: 4485
-
- Fixed issue where the :class:`_types.JSON` type had a read-only
- :attr:`_types.JSON.should_evaluate_none` attribute, which would cause failures
- when making use of the :meth:`.TypeEngine.evaluates_none` method in
- conjunction with this type. Pull request courtesy Sanjana S.
-
- .. change::
- :tags: bug, mssql
- :tickets: 4499
-
- Fixed bug where the SQL Server "IDENTITY_INSERT" logic that allows an INSERT
- to proceed with an explicit value on an IDENTITY column was not detecting
- the case where :meth:`_expression.Insert.values` were used with a dictionary that
- contained a :class:`_schema.Column` as key and a SQL expression as a value.
-
- .. change::
- :tags: bug, sqlite
- :tickets: 4474
-
- Fixed bug in SQLite DDL where using an expression as a server side default
- required that it be contained within parenthesis to be accepted by the
- sqlite parser. Pull request courtesy Bartlomiej Biernacki.
-
- .. change::
- :tags: bug, mysql
- :tickets: 4492
-
- Fixed a second regression caused by :ticket:`4344` (the first was
- :ticket:`4361`), which works around MySQL issue 88718, where the lower
- casing function used was not correct for Python 2 with OSX/Windows casing
- conventions, which would then raise ``TypeError``. Full coverage has been
- added to this logic so that every codepath is exercised in a mock style for
- all three casing conventions on all versions of Python. MySQL 8.0 has
- meanwhile fixed issue 88718 so the workaround is only applies to a
- particular span of MySQL 8.0 versions.
-
-.. changelog::
- :version: 1.2.17
- :released: January 25, 2019
-
- .. change::
- :tags: feature, orm
- :tickets: 4461
-
- Added new event hooks :meth:`.QueryEvents.before_compile_update` and
- :meth:`.QueryEvents.before_compile_delete` which complement
- :meth:`.QueryEvents.before_compile` in the case of the :meth:`_query.Query.update`
- and :meth:`_query.Query.delete` methods.
-
-
- .. change::
- :tags: bug, postgresql
- :tickets: 4463
-
- Revised the query used when reflecting CHECK constraints to make use of the
- ``pg_get_constraintdef`` function, as the ``consrc`` column is being
- deprecated in PG 12. Thanks to John A Stevenson for the tip.
-
-
- .. change::
- :tags: bug, orm
- :tickets: 4454
-
- Fixed issue where when using single-table inheritance in conjunction with a
- joined inheritance hierarchy that uses "with polymorphic" loading, the
- "single table criteria" for that single-table entity could get confused for
- that of other entities from the same hierarchy used in the same query.The
- adaption of the "single table criteria" is made more specific to the target
- entity to avoid it accidentally getting adapted to other tables in the
- query.
-
-
- .. change::
- :tags: bug, oracle
- :tickets: 4457
-
- Fixed regression in integer precision logic due to the refactor of the
- cx_Oracle dialect in 1.2. We now no longer apply the cx_Oracle.NATIVE_INT
- type to result columns sending integer values (detected as positive
- precision with scale ==0) which encounters integer overflow issues with
- values that go beyond the 32 bit boundary. Instead, the output variable
- is left untyped so that cx_Oracle can choose the best option.
-
-.. changelog::
- :version: 1.2.16
- :released: January 11, 2019
-
- .. change::
- :tag: bug, sql
- :tickets: 4394
-
- Fixed issue in "expanding IN" feature where using the same bound parameter
- name more than once in a query would lead to a KeyError within the process
- of rewriting the parameters in the query.
-
- .. change::
- :tags: bug, postgresql
- :tickets: 4416
-
- Fixed issue where a :class:`_postgresql.ENUM` or a custom domain present
- in a remote schema would not be recognized within column reflection if
- the name of the enum/domain or the name of the schema required quoting.
- A new parsing scheme now fully parses out quoted or non-quoted tokens
- including support for SQL-escaped quotes.
-
- .. change::
- :tags: bug, postgresql
-
- Fixed issue where multiple :class:`_postgresql.ENUM` objects referred to
- by the same :class:`_schema.MetaData` object would fail to be created if
- multiple objects had the same name under different schema names. The
- internal memoization the PostgreSQL dialect uses to track if it has
- created a particular :class:`_postgresql.ENUM` in the database during
- a DDL creation sequence now takes schema name into account.
-
- .. change::
- :tags: bug, engine
- :tickets: 4429
-
- Fixed a regression introduced in version 1.2 where a refactor
- of the :class:`.SQLAlchemyError` base exception class introduced an
- inappropriate coercion of a plain string message into Unicode under
- python 2k, which is not handled by the Python interpreter for characters
- outside of the platform's encoding (typically ascii). The
- :class:`.SQLAlchemyError` class now passes a bytestring through under
- Py2K for ``__str__()`` as is the behavior of exception objects in general
- under Py2K, does a safe coercion to unicode utf-8 with
- backslash fallback for ``__unicode__()``. For Py3K the message is
- typically unicode already, but if not is again safe-coerced with utf-8
- with backslash fallback for the ``__str__()`` method.
-
- .. change::
- :tags: bug, sql, oracle, mysql
- :tickets: 4436
-
- Fixed issue where the DDL emitted for :class:`.DropTableComment`, which
- will be used by an upcoming version of Alembic, was incorrect for the MySQL
- and Oracle databases.
-
- .. change::
- :tags: bug, sqlite
- :tickets: 4431
-
- Reflection of an index based on SQL expressions are now skipped with a
- warning, in the same way as that of the Postgresql dialect, where we currently
- do not support reflecting indexes that have SQL expressions within them.
- Previously, an index with columns of None were produced which would break
- tools like Alembic.
-
-.. changelog::
- :version: 1.2.15
- :released: December 11, 2018
-
- .. change::
- :tags: bug, orm
- :tickets: 4367
-
- Fixed bug where the ORM annotations could be incorrect for the
- primaryjoin/secondaryjoin a relationship if one used the pattern
- ``ForeignKey(SomeClass.id)`` in the declarative mappings. This pattern
- would leak undesired annotations into the join conditions which can break
- aliasing operations done within :class:`_query.Query` that are not supposed to
- impact elements in that join condition. These annotations are now removed
- up front if present.
-
- .. change::
- :tags: bug, orm, declarative
- :tickets: 4374
-
- A warning is emitted in the case that a :func:`_expression.column` object is applied to
- a declarative class, as it seems likely this intended to be a
- :class:`_schema.Column` object.
-
- .. change::
- :tags: bug, orm
- :tickets: 4366
-
- In continuing with a similar theme as that of very recent :ticket:`4349`,
- repaired issue with :meth:`.RelationshipProperty.Comparator.any` and
- :meth:`.RelationshipProperty.Comparator.has` where the "secondary"
- selectable needs to be explicitly part of the FROM clause in the
- EXISTS subquery to suit the case where this "secondary" is a :class:`_expression.Join`
- object.
-
- .. change::
- :tags: bug, orm
- :tickets: 4363
-
- Fixed regression caused by :ticket:`4349` where adding the "secondary"
- table to the FROM clause for a dynamic loader would affect the ability of
- the :class:`_query.Query` to make a subsequent join to another entity. The fix
- adds the primary entity as the first element of the FROM list since
- :meth:`_query.Query.join` wants to jump from that. Version 1.3 will have
- a more comprehensive solution to this problem as well (:ticket:`4365`).
-
-
-
-
- .. change::
- :tags: bug, orm
- :tickets: 4400
-
- Fixed bug where chaining of mapper options using
- :meth:`.RelationshipProperty.of_type` in conjunction with a chained option
- that refers to an attribute name by string only would fail to locate the
- attribute.
-
- .. change::
- :tag: feature, mysql
- :tickets: 4381
-
- Added support for the ``write_timeout`` flag accepted by mysqlclient and
- pymysql to be passed in the URL string.
-
- .. change::
- :tag: bug, postgresql
- :tickets: 4377, 4380
-
- Fixed issue where reflection of a PostgreSQL domain that is expressed as an
- array would fail to be recognized. Pull request courtesy Jakub Synowiec.
-
-
-.. changelog::
- :version: 1.2.14
- :released: November 10, 2018
-
- .. change::
- :tags: bug, orm
- :tickets: 4357
-
- Fixed bug in :meth:`.Session.bulk_update_mappings` where alternate mapped
- attribute names would result in the primary key column of the UPDATE
- statement being included in the SET clause, as well as the WHERE clause;
- while usually harmless, for SQL Server this can raise an error due to the
- IDENTITY column. This is a continuation of the same bug that was fixed in
- :ticket:`3849`, where testing was insufficient to catch this additional
- flaw.
-
- .. change::
- :tags: bug, mysql
- :tickets: 4361
-
- Fixed regression caused by :ticket:`4344` released in 1.2.13, where the fix
- for MySQL 8.0's case sensitivity problem with referenced column names when
- reflecting foreign key referents is worked around using the
- ``information_schema.columns`` view. The workaround was failing on OSX /
- ``lower_case_table_names=2`` which produces non-matching casing for the
- ``information_schema.columns`` vs. that of ``SHOW CREATE TABLE``, so in
- case-insensitive SQL modes case-insensitive matching is now used.
-
- .. change::
- :tags: bug, orm
- :tickets: 4347
-
- Fixed a minor performance issue which could in some cases add unnecessary
- overhead to result fetching, involving the use of ORM columns and entities
- that include those same columns at the same time within a query. The issue
- has to do with hash / eq overhead when referring to the column in different
- ways.
-
-.. changelog::
- :version: 1.2.13
- :released: October 31, 2018
-
- .. change::
- :tags: bug, postgresql
- :tickets: 4337
-
- Added support for the :class:`.aggregate_order_by` function to receive
- multiple ORDER BY elements, previously only a single element was accepted.
-
-
- .. change::
- :tags: bug, mysql
- :tickets: 4348
-
- Added word ``function`` to the list of reserved words for MySQL, which is
- now a keyword in MySQL 8.0
-
- .. change::
- :tags: feature, sql
- :versions: 1.3.0b1
-
- Refactored :class:`.SQLCompiler` to expose a
- :meth:`.SQLCompiler.group_by_clause` method similar to the
- :meth:`.SQLCompiler.order_by_clause` and :meth:`.SQLCompiler.limit_clause`
- methods, which can be overridden by dialects to customize how GROUP BY
- renders. Pull request courtesy Samuel Chou.
-
- .. change::
- :tags: bug, misc
-
- Fixed issue where part of the utility language helper internals was passing
- the wrong kind of argument to the Python ``__import__`` builtin as the list
- of modules to be imported. The issue produced no symptoms within the core
- library but could cause issues with external applications that redefine the
- ``__import__`` builtin or otherwise instrument it. Pull request courtesy Joe
- Urciuoli.
-
- .. change::
- :tags: bug, orm
- :tickets: 4349
-
- Fixed bug where "dynamic" loader needs to explicitly set the "secondary"
- table in the FROM clause of the query, to suit the case where the secondary
- is a join object that is otherwise not pulled into the query from its
- columns alone.
-
-
- .. change::
- :tags: bug, orm, declarative
- :tickets: 4350
-
- Fixed regression caused by :ticket:`4326` in version 1.2.12 where using
- :class:`.declared_attr` with a mixin in conjunction with
- :func:`_orm.synonym` would fail to map the synonym properly to an inherited
- subclass.
-
- .. change::
- :tags: bug, misc, py3k
- :tickets: 4339
-
- Fixed additional warnings generated by Python 3.7 due to changes in the
- organization of the Python ``collections`` and ``collections.abc`` packages.
- Previous ``collections`` warnings were fixed in version 1.2.11. Pull request
- courtesy xtreak.
-
- .. change::
- :tags: bug, ext
-
- Added missing ``.index()`` method to list-based association collections
- in the association proxy extension.
-
- .. change::
- :tags: bug, mysql
- :tickets: 4344
-
- Added a workaround for a MySQL bug #88718 introduced in the 8.0 series,
- where the reflection of a foreign key constraint is not reporting the
- correct case sensitivity for the referred column, leading to errors during
- use of the reflected constraint such as when using the automap extension.
- The workaround emits an additional query to the information_schema tables in
- order to retrieve the correct case sensitive name.
-
- .. change::
- :tags: bug, sql
- :tickets: 4341
-
- Fixed bug where the :paramref:`.Enum.create_constraint` flag on the
- :class:`.Enum` datatype would not be propagated to copies of the type, which
- affects use cases such as declarative mixins and abstract bases.
-
- .. change::
- :tags: bug, orm, declarative
- :tickets: 4352
-
- The column conflict resolution technique discussed at
- :ref:`orm_inheritance_column_conflicts` is now functional for a :class:`_schema.Column`
- that is also a primary key column. Previously, a check for primary key
- columns declared on a single-inheritance subclass would occur before the
- column copy were allowed to pass.
-
-
-.. changelog::
- :version: 1.2.12
- :released: September 19, 2018
-
- .. change::
- :tags: bug, postgresql
- :tickets: 4325
-
- Fixed bug in PostgreSQL dialect where compiler keyword arguments such as
- ``literal_binds=True`` were not being propagated to a DISTINCT ON
- expression.
-
- .. change::
- :tags: bug, ext
- :tickets: 4328
-
- Fixed issue where :class:`.BakedQuery` did not include the specific query
- class used by the :class:`.Session` as part of the cache key, leading to
- incompatibilities when using custom query classes, in particular the
- :class:`.ShardedQuery` which has some different argument signatures.
-
- .. change::
- :tags: bug, postgresql
- :tickets: 4324
-
- Fixed the :func:`_postgresql.array_agg` function, which is a slightly
- altered version of the usual :func:`_functions.array_agg` function, to also
- accept an incoming "type" argument without forcing an ARRAY around it,
- essentially the same thing that was fixed for the generic function in 1.1
- in :ticket:`4107`.
-
- .. change::
- :tags: bug, postgresql
- :tickets: 4323
-
- Fixed bug in PostgreSQL ENUM reflection where a case-sensitive, quoted name
- would be reported by the query including quotes, which would not match a
- target column during table reflection as the quotes needed to be stripped
- off.
-
-
- .. change::
- :tags: bug, orm
-
- Added a check within the weakref cleanup for the :class:`.InstanceState`
- object to check for the presence of the ``dict`` builtin, in an effort to
- reduce error messages generated when these cleanups occur during interpreter
- shutdown. Pull request courtesy Romuald Brunet.
-
- .. change::
- :tags: bug, orm, declarative
- :tickets: 4326
-
- Fixed bug where the declarative scan for attributes would receive the
- expression proxy delivered by a hybrid attribute at the class level, and
- not the hybrid attribute itself, when receiving the descriptor via the
- ``@declared_attr`` callable on a subclass of an already-mapped class. This
- would lead to an attribute that did not report itself as a hybrid when
- viewed within :attr:`_orm.Mapper.all_orm_descriptors`.
-
-
- .. change::
- :tags: bug, orm
- :tickets: 4334
- :versions: 1.3.0b1
-
- Fixed bug where use of :class:`_expression.Lateral` construct in conjunction with
- :meth:`_query.Query.join` as well as :meth:`_query.Query.select_entity_from` would not
- apply clause adaption to the right side of the join. "lateral" introduces
- the use case of the right side of a join being correlatable. Previously,
- adaptation of this clause wasn't considered. Note that in 1.2 only,
- a selectable introduced by :meth:`_query.Query.subquery` is still not adapted
- due to :ticket:`4304`; the selectable needs to be produced by the
- :func:`_expression.select` function to be the right side of the "lateral" join.
-
- .. change::
- :tags: bug, oracle
- :tickets: 4335
-
- Fixed issue for cx_Oracle 7.0 where the behavior of Oracle param.getvalue()
- now returns a list, rather than a single scalar value, breaking
- autoincrement logic throughout the Core and ORM. The dml_ret_array_val
- compatibility flag is used for cx_Oracle 6.3 and 6.4 to establish compatible
- behavior with 7.0 and forward, for cx_Oracle 6.2.1 and prior a version
- number check falls back to the old logic.
-
-
- .. change::
- :tags: bug, orm
- :tickets: 4327
-
- Fixed 1.2 regression caused by :ticket:`3472` where the handling of an
- "updated_at" style column within the context of a post-update operation
- would also occur for a row that is to be deleted following the update,
- meaning both that a column with a Python-side value generator would show
- the now-deleted value that was emitted for the UPDATE before the DELETE
- (which was not the previous behavior), as well as that a SQL- emitted value
- generator would have the attribute expired, meaning the previous value
- would be unreachable due to the row having been deleted and the object
- detached from the session.The "postfetch" logic that was added as part of
- :ticket:`3472` is now skipped entirely for an object that ultimately is to
- be deleted.
-
-.. changelog::
- :version: 1.2.11
- :released: August 20, 2018
-
- .. change::
- :tags: bug, py3k
-
- Started importing "collections" from "collections.abc" under Python 3.3 and
- greater for Python 3.8 compatibility. Pull request courtesy Nathaniel
- Knight.
-
- .. change::
- :tag: bug, sqlite
-
- Fixed issue where the "schema" name used for a SQLite database within table
- reflection would not quote the schema name correctly. Pull request
- courtesy Phillip Cloud.
-
- .. change::
- :tags: bug, sql
- :tickets: 4320
-
- Fixed issue that is closely related to :ticket:`3639` where an expression
- rendered in a boolean context on a non-native boolean backend would
- be compared to 1/0 even though it is already an implicitly boolean
- expression, when :meth:`_expression.ColumnElement.self_group` were used. While this
- does not affect the user-friendly backends (MySQL, SQLite) it was not
- handled by Oracle (and possibly SQL Server). Whether or not the
- expression is implicitly boolean on any database is now determined
- up front as an additional check to not generate the integer comparison
- within the compilation of the statement.
-
- .. change::
- :tags: bug, oracle
- :tickets: 4309
-
- For cx_Oracle, Integer datatypes will now be bound to "int", per advice
- from the cx_Oracle developers. Previously, using cx_Oracle.NUMBER caused a
- loss in precision within the cx_Oracle 6.x series.
-
-
- .. change::
- :tags: bug, orm, declarative
- :tickets: 4321
-
- Fixed issue in previously untested use case, allowing a declarative mapped
- class to inherit from a classically-mapped class outside of the declarative
- base, including that it accommodates for unmapped intermediate classes. An
- unmapped intermediate class may specify ``__abstract__``, which is now
- interpreted correctly, or the intermediate class can remain unmarked, and
- the classically mapped base class will be detected within the hierarchy
- regardless. In order to anticipate existing scenarios which may be mixing
- in classical mappings into existing declarative hierarchies, an error is
- now raised if multiple mapped bases are detected for a given class.
-
- .. change::
- :tags: bug, sql
- :tickets: 4322
-
- Added missing window function parameters
- :paramref:`.WithinGroup.over.range_` and :paramref:`.WithinGroup.over.rows`
- parameters to the :meth:`.WithinGroup.over` and
- :meth:`.FunctionFilter.over` methods, to correspond to the range/rows
- feature added to the "over" method of SQL functions as part of
- :ticket:`3049` in version 1.1.
-
- .. change::
- :tags: bug, sql
- :tickets: 4313
-
- Fixed bug where the multi-table support for UPDATE and DELETE statements
- did not consider the additional FROM elements as targets for correlation,
- when a correlated SELECT were also combined with the statement. This
- change now includes that a SELECT statement in the WHERE clause for such a
- statement will try to auto-correlate back to these additional tables in the
- parent UPDATE/DELETE or unconditionally correlate if
- :meth:`_expression.Select.correlate` is used. Note that auto-correlation raises an
- error if the SELECT statement would have no FROM clauses as a result, which
- can now occur if the parent UPDATE/DELETE specifies the same tables in its
- additional set of tables; specify :meth:`_expression.Select.correlate` explicitly to
- resolve.
-
-.. changelog::
- :version: 1.2.10
- :released: July 13, 2018
-
- .. change::
- :tags: bug, sql
- :tickets: 4300
-
- Fixed bug where a :class:`.Sequence` would be dropped explicitly before any
- :class:`_schema.Table` that refers to it, which breaks in the case when the
- sequence is also involved in a server-side default for that table, when
- using :meth:`_schema.MetaData.drop_all`. The step which processes sequences
- to be dropped via non server-side column default functions is now invoked
- after the table itself is dropped.
-
- .. change::
- :tags: bug, orm
- :tickets: 4295
-
- Fixed bug in :class:`.Bundle` construct where placing two columns of the
- same name would be de-duplicated, when the :class:`.Bundle` were used as
- part of the rendered SQL, such as in the ORDER BY or GROUP BY of the statement.
-
-
- .. change::
- :tags: bug, orm
- :tickets: 4298
-
- Fixed regression in 1.2.9 due to :ticket:`4287` where using a
- :class:`_orm.Load` option in conjunction with a string wildcard would result
- in a TypeError.
-
-.. changelog::
- :version: 1.2.9
- :released: June 29, 2018
-
- .. change::
- :tags: bug, mysql
-
- Fixed percent-sign doubling in mysql-connector-python dialect, which does
- not require de-doubling of percent signs. Additionally, the mysql-
- connector-python driver is inconsistent in how it passes the column names
- in cursor.description, so a workaround decoder has been added to
- conditionally decode these randomly-sometimes-bytes values to unicode only
- if needed. Also improved test support for mysql-connector-python, however
- it should be noted that this driver still has issues with unicode that
- continue to be unresolved as of yet.
-
-
- .. change::
- :tags: bug, mssql
- :tickets: 4288
-
- Fixed bug in MSSQL reflection where when two same-named tables in different
- schemas had same-named primary key constraints, foreign key constraints
- referring to one of the tables would have their columns doubled, causing
- errors. Pull request courtesy Sean Dunn.
-
- .. change::
- :tags: bug, sql
- :tickets: 4279
-
- Fixed regression in 1.2 due to :ticket:`4147` where a :class:`_schema.Table` that
- has had some of its indexed columns redefined with new ones, as would occur
- when overriding columns during reflection or when using
- :paramref:`_schema.Table.extend_existing`, such that the :meth:`_schema.Table.tometadata`
- method would fail when attempting to copy those indexes as they still
- referred to the replaced column. The copy logic now accommodates for this
- condition.
-
-
- .. change::
- :tags: bug, mysql
- :tickets: 4293
-
- Fixed bug in index reflection where on MySQL 8.0 an index that includes
- ASC or DESC in an indexed column specification would not be correctly
- reflected, as MySQL 8.0 introduces support for returning this information
- in a table definition string.
-
- .. change::
- :tags: bug, orm
- :tickets: 3505
-
- Fixed issue where chaining multiple join elements inside of
- :meth:`_query.Query.join` might not correctly adapt to the previous left-hand
- side, when chaining joined inheritance classes that share the same base
- class.
-
- .. change::
- :tags: bug, orm
- :tickets: 4287
-
- Fixed bug in cache key generation for baked queries which could cause a
- too-short cache key to be generated for the case of eager loads across
- subclasses. This could in turn cause the eagerload query to be cached in
- place of a non-eagerload query, or vice versa, for a polymorphic "selectin"
- load, or possibly for lazy loads or selectin loads as well.
-
- .. change::
- :tags: bug, sqlite
-
- Fixed issue in test suite where SQLite 3.24 added a new reserved word that
- conflicted with a usage in TypeReflectionTest. Pull request courtesy Nils
- Philippsen.
-
- .. change::
- :tags: feature, oracle
- :tickets: 4290
- :versions: 1.3.0b1
-
- Added a new event currently used only by the cx_Oracle dialect,
- :meth:`.DialectEvents.setiputsizes`. The event passes a dictionary of
- :class:`.BindParameter` objects to DBAPI-specific type objects that will be
- passed, after conversion to parameter names, to the cx_Oracle
- ``cursor.setinputsizes()`` method. This allows both visibility into the
- setinputsizes process as well as the ability to alter the behavior of what
- datatypes are passed to this method.
-
- .. seealso::
-
- :ref:`cx_oracle_setinputsizes`
-
- .. change::
- :tags: bug, orm
- :tickets: 4286
-
- Fixed bug in new polymorphic selectin loading where the BakedQuery used
- internally would be mutated by the given loader options, which would both
- inappropriately mutate the subclass query as well as carry over the effect
- to subsequent queries.
-
- .. change::
- :tags: bug, py3k
- :tickets: 4291
-
- Replaced the usage of inspect.formatargspec() with a vendored version
- copied from the Python standard library, as inspect.formatargspec()
- is deprecated and as of Python 3.7.0 is emitting a warning.
-
- .. change::
- :tags: feature, ext
- :tickets: 4243
- :versions: 1.3.0b1
-
- Added new attribute :attr:`_query.Query.lazy_loaded_from` which is populated
- with an :class:`.InstanceState` that is using this :class:`_query.Query` in
- order to lazy load a relationship. The rationale for this is that
- it serves as a hint for the horizontal sharding feature to use, such that
- the identity token of the state can be used as the default identity token
- to use for the query within id_chooser().
-
- .. change::
- :tags: bug, mysql
- :tickets: 4283
-
- Fixed bug in MySQLdb dialect and variants such as PyMySQL where an
- additional "unicode returns" check upon connection makes explicit use of
- the "utf8" character set, which in MySQL 8.0 emits a warning that utf8mb4
- should be used. This is now replaced with a utf8mb4 equivalent.
- Documentation is also updated for the MySQL dialect to specify utf8mb4 in
- all examples. Additional changes have been made to the test suite to use
- utf8mb3 charsets and databases (there seem to be collation issues in some
- edge cases with utf8mb4), and to support configuration default changes made
- in MySQL 8.0 such as explicit_defaults_for_timestamp as well as new errors
- raised for invalid MyISAM indexes.
-
-
-
- .. change::
- :tags: bug, mysql
- :tickets: 3645
-
- The :class:`_expression.Update` construct now accommodates a :class:`_expression.Join` object
- as supported by MySQL for UPDATE..FROM. As the construct already
- accepted an alias object for a similar purpose, the feature of UPDATE
- against a non-table was already implied so this has been added.
-
- .. change::
- :tags: bug, mssql, py3k
- :tickets: 4273
-
- Fixed issue within the SQL Server dialect under Python 3 where when running
- against a non-standard SQL server database that does not contain either the
- "sys.dm_exec_sessions" or "sys.dm_pdw_nodes_exec_sessions" views, leading
- to a failure to fetch the isolation level, the error raise would fail due
- to an UnboundLocalError.
-
-
-
- .. change::
- :tags: bug, orm
- :tickets: 4269
-
- Fixed regression caused by :ticket:`4256` (itself a regression fix for
- :ticket:`4228`) which breaks an undocumented behavior which converted for a
- non-sequence of entities passed directly to the :class:`_query.Query` constructor
- into a single-element sequence. While this behavior was never supported or
- documented, it's already in use so has been added as a behavioral contract
- to :class:`_query.Query`.
-
- .. change::
- :tags: bug, orm
- :tickets: 4270
-
- Fixed an issue that was both a performance regression in 1.2 as well as an
- incorrect result regarding the "baked" lazy loader, involving the
- generation of cache keys from the original :class:`_query.Query` object's loader
- options. If the loader options were built up in a "branched" style using
- common base elements for multiple options, the same options would be
- rendered into the cache key repeatedly, causing both a performance issue as
- well as generating the wrong cache key. This is fixed, along with a
- performance improvement when such "branched" options are applied via
- :meth:`_query.Query.options` to prevent the same option objects from being
- applied repeatedly.
-
- .. change::
- :tags: bug, oracle, mysql
- :tickets: 4275
-
- Fixed INSERT FROM SELECT with CTEs for the Oracle and MySQL dialects, where
- the CTE was being placed above the entire statement as is typical with
- other databases, however Oracle and MariaDB 10.2 wants the CTE underneath
- the "INSERT" segment. Note that the Oracle and MySQL dialects don't yet
- work when a CTE is applied to a subquery inside of an UPDATE or DELETE
- statement, as the CTE is still applied to the top rather than inside the
- subquery.
-
-
-.. changelog::
- :version: 1.2.8
- :released: May 28, 2018
-
- .. change::
- :tags: bug, orm
- :tickets: 4256
-
- Fixed regression in 1.2.7 caused by :ticket:`4228`, which itself was fixing
- a 1.2-level regression, where the ``query_cls`` callable passed to a
- :class:`.Session` was assumed to be a subclass of :class:`_query.Query` with
- class method availability, as opposed to an arbitrary callable. In
- particular, the dogpile caching example illustrates ``query_cls`` as a
- function and not a :class:`_query.Query` subclass.
-
- .. change::
- :tags: bug, engine
- :tickets: 4252
-
- Fixed connection pool issue whereby if a disconnection error were raised
- during the connection pool's "reset on return" sequence in conjunction with
- an explicit transaction opened against the enclosing :class:`_engine.Connection`
- object (such as from calling :meth:`.Session.close` without a rollback or
- commit, or calling :meth:`_engine.Connection.close` without first closing a
- transaction declared with :meth:`_engine.Connection.begin`), a double-checkin would
- result, which could then lead towards concurrent checkouts of the same
- connection. The double-checkin condition is now prevented overall by an
- assertion, as well as the specific double-checkin scenario has been
- fixed.
-
- .. change::
- :tags: bug, oracle
- :tickets: 4264
-
- The Oracle BINARY_FLOAT and BINARY_DOUBLE datatypes now participate within
- cx_Oracle.setinputsizes(), passing along NATIVE_FLOAT, so as to support the
- NaN value. Additionally, :class:`_oracle.BINARY_FLOAT`,
- :class:`_oracle.BINARY_DOUBLE` and :class:`_oracle.DOUBLE_PRECISION` now
- subclass :class:`.Float`, since these are floating point datatypes, not
- decimal. These datatypes were already defaulting the
- :paramref:`.Float.asdecimal` flag to False in line with what
- :class:`.Float` already does.
-
- .. change::
- :tags: bug, oracle
-
- Added reflection capabilities for the :class:`_oracle.BINARY_FLOAT`,
- :class:`_oracle.BINARY_DOUBLE` datatypes.
-
-
- .. change::
- :tags: bug, ext
- :tickets: 4247
-
- The horizontal sharding extension now makes use of the identity token
- added to ORM identity keys as part of :ticket:`4137`, when an object
- refresh or column-based deferred load or unexpiration operation occurs.
- Since we know the "shard" that the object originated from, we make
- use of this value when refreshing, thereby avoiding queries against
- other shards that don't match this object's identity in any case.
-
- .. change::
- :tags: bug, sql
-
- Fixed issue where the "ambiguous literal" error message used when
- interpreting literal values as SQL expression values would encounter a
- tuple value, and fail to format the message properly. Pull request courtesy
- Miguel Ventura.
-
- .. change::
- :tags: bug, mssql
- :tickets: 4250
-
- Fixed a 1.2 regression caused by :ticket:`4061` where the SQL Server
- "BIT" type would be considered to be "native boolean". The goal here
- was to avoid creating a CHECK constraint on the column, however the bigger
- issue is that the BIT value does not behave like a true/false constant
- and cannot be interpreted as a standalone expression, e.g.
- "WHERE <column>". The SQL Server dialect now goes back to being
- non-native boolean, but with an extra flag that still avoids creating
- the CHECK constraint.
-
- .. change::
- :tags: bug, oracle
- :tickets: 4259
-
- Altered the Oracle dialect such that when an :class:`.Integer` type is in
- use, the cx_Oracle.NUMERIC type is set up for setinputsizes(). In
- SQLAlchemy 1.1 and earlier, cx_Oracle.NUMERIC was passed for all numeric
- types unconditionally, and in 1.2 this was removed to allow for better
- numeric precision. However, for integers, some database/client setups
- will fail to coerce boolean values True/False into integers which introduces
- regressive behavior when using SQLAlchemy 1.2. Overall, the setinputsizes
- logic seems like it will need a lot more flexibility going forward so this
- is a start for that.
-
- .. change::
- :tags: bug, engine
-
- Fixed a reference leak issue where the values of the parameter dictionary
- used in a statement execution would remain referenced by the "compiled
- cache", as a result of storing the key view used by Python 3 dictionary
- keys(). Pull request courtesy Olivier Grisel.
-
- .. change::
- :tags: bug, orm
- :tickets: 4128
-
- Fixed a long-standing regression that occurred in version
- 1.0, which prevented the use of a custom :class:`.MapperOption`
- that alters the _params of a :class:`_query.Query` object for a
- lazy load, since the lazy loader itself would overwrite those
- parameters. This applies to the "temporal range" example
- on the wiki. Note however that the
- :meth:`_query.Query.populate_existing` method is now required in
- order to rewrite the mapper options associated with an object
- already loaded in the identity map.
-
- As part of this change, a custom defined
- :class:`.MapperOption` will now cause lazy loaders related to
- the target object to use a non-baked query by default unless
- the :meth:`.MapperOption._generate_cache_key` method is implemented.
- In particular, this repairs one regression which occurred when
- using the dogpile.cache "advanced" example, which was not
- returning cached results and instead emitting SQL due to an
- incompatibility with the baked query loader; with the change,
- the ``RelationshipCache`` option included for many releases
- in the dogpile example will disable the "baked" query altogether.
- Note that the dogpile example is also modernized to avoid both
- of these issues as part of issue :ticket:`4258`.
-
- .. change::
- :tags: bug, ext
- :tickets: 4266
-
- Fixed a race condition which could occur if automap
- :meth:`.AutomapBase.prepare` were used within a multi-threaded context
- against other threads which may call :func:`.configure_mappers` as a
- result of use of other mappers. The unfinished mapping work of automap
- is particularly sensitive to being pulled in by a
- :func:`.configure_mappers` step leading to errors.
-
- .. change::
- :tags: bug, orm
-
- Fixed bug where the new :meth:`.baked.Result.with_post_criteria`
- method would not interact with a subquery-eager loader correctly,
- in that the "post criteria" would not be applied to embedded
- subquery eager loaders. This is related to :ticket:`4128` in that
- the post criteria feature is now used by the lazy loader.
-
- .. change::
- :tags: bug, tests
- :tickets: 4249
-
- Fixed a bug in the test suite where if an external dialect returned
- ``None`` for ``server_version_info``, the exclusion logic would raise an
- ``AttributeError``.
-
- .. change::
- :tags: bug, orm
- :tickets: 4258
-
- Updated the dogpile.caching example to include new structures that
- accommodate for the "baked" query system, which is used by default within
- lazy loaders and some eager relationship loaders. The dogpile.caching
- "relationship_caching" and "advanced" examples were also broken due to
- :ticket:`4256`. The issue here is also worked-around by the fix in
- :ticket:`4128`.
-
-.. changelog::
- :version: 1.2.7
- :released: April 20, 2018
-
- .. change::
- :tags: bug, orm
- :tickets: 4228
-
- Fixed regression in 1.2 within sharded query feature where the
- new "identity_token" element was not being correctly considered within
- the scope of a lazy load operation, when searching the identity map
- for a related many-to-one element. The new behavior will allow for
- making use of the "id_chooser" in order to determine the best identity
- key to retrieve from the identity map. In order to achieve this, some
- refactoring of 1.2's "identity_token" approach has made some slight changes
- to the implementation of ``ShardedQuery`` which should be noted for other
- derivations of this class.
-
- .. change::
- :tags: bug, postgresql
- :tickets: 4229
-
- Fixed bug where the special "not equals" operator for the PostgreSQL
- "range" datatypes such as DATERANGE would fail to render "IS NOT NULL" when
- compared to the Python ``None`` value.
-
-
-
- .. change::
- :tags: bug, mssql
- :tickets: 4234
-
- Fixed 1.2 regression caused by :ticket:`4060` where the query used to
- reflect SQL Server cross-schema foreign keys was limiting the criteria
- incorrectly.
-
-
-
- .. change::
- :tags: bug, oracle
-
- The Oracle NUMBER datatype is reflected as INTEGER if the precision is NULL
- and the scale is zero, as this is how INTEGER values come back when
- reflected from Oracle's tables. Pull request courtesy Kent Bower.
-
- .. change::
- :tags: feature, postgresql
- :tickets: 4160
- :versions: 1.3.0b1
-
- Added new PG type :class:`_postgresql.REGCLASS` which assists in casting
- table names to OID values. Pull request courtesy Sebastian Bank.
-
- .. change::
- :tags: bug, sql
- :tickets: 4231
-
- Fixed issue where the compilation of an INSERT statement with the
- "literal_binds" option that also uses an explicit sequence and "inline"
- generation, as on PostgreSQL and Oracle, would fail to accommodate the
- extra keyword argument within the sequence processing routine.
-
- .. change::
- :tags: bug, orm
- :tickets: 4241
-
- Fixed issue in single-inheritance loading where the use of an aliased
- entity against a single-inheritance subclass in conjunction with the
- :meth:`_query.Query.select_from` method would cause the SQL to be rendered with
- the unaliased table mixed in to the query, causing a cartesian product. In
- particular this was affecting the new "selectin" loader when used against a
- single-inheritance subclass.
-
-.. changelog::
- :version: 1.2.6
- :released: March 30, 2018
-
- .. change::
- :tags: bug, mssql
- :tickets: 4227
-
- Adjusted the SQL Server version detection for pyodbc to only allow for
- numeric tokens, filtering out non-integers, since the dialect does tuple-
- numeric comparisons with this value. This is normally true for all known
- SQL Server / pyodbc drivers in any case.
-
- .. change::
- :tags: feature, postgresql
-
- Added support for "PARTITION BY" in PostgreSQL table definitions,
- using "postgresql_partition_by". Pull request courtesy
- Vsevolod Solovyov.
-
- .. change::
- :tags: bug, sql
- :tickets: 4204
-
- Fixed a regression that occurred from the previous fix to :ticket:`4204` in
- version 1.2.5, where a CTE that refers to itself after the
- :meth:`_expression.CTE.alias` method has been called would not refer to itself
- correctly.
-
- .. change::
- :tags: bug, engine
- :tickets: 4225
-
- Fixed bug in connection pool where a connection could be present in the
- pool without all of its "connect" event handlers called, if a previous
- "connect" handler threw an exception; note that the dialects themselves
- have connect handlers that emit SQL, such as those which set transaction
- isolation, which can fail if the database is in a non-available state, but
- still allows a connection. The connection is now invalidated first if any
- of the connect handlers fail.
-
- .. change::
- :tags: bug, oracle
- :tickets: 4211
-
- The minimum cx_Oracle version supported is 5.2 (June 2015). Previously,
- the dialect asserted against version 5.0 but as of 1.2.2 we are using some
- symbols that did not appear until 5.2.
-
- .. change::
- :tags: bug, declarative
- :tickets: 4221
-
- Removed a warning that would be emitted when calling upon
- ``__table_args__``, ``__mapper_args__`` as named with a ``@declared_attr``
- method, when called from a non-mapped declarative mixin. Calling these
- directly is documented as the approach to use when one is overriding one
- of these methods on a mapped class. The warning still emits for regular
- attribute names.
-
- .. change::
- :tags: bug, orm
- :tickets: 4215
-
- Fixed bug where using :meth:`.Mutable.associate_with` or
- :meth:`.Mutable.as_mutable` in conjunction with a class that has non-
- primary mappers set up with alternatively-named attributes would produce an
- attribute error. Since non-primary mappers are not used for persistence,
- the mutable extension now excludes non-primary mappers from its
- instrumentation steps.
-
-
-.. changelog::
- :version: 1.2.5
- :released: March 6, 2018
-
- .. change::
- :tags: bug, sql
- :tickets: 4210
-
- Fixed bug in :class:.`CTE` construct along the same lines as that of
- :ticket:`4204` where a :class:`_expression.CTE` that was aliased would not copy itself
- correctly during a "clone" operation as is frequent within the ORM as well
- as when using the :meth:`_expression.ClauseElement.params` method.
-
- .. change::
- :tags: bug, orm
- :tickets: 4199
-
- Fixed bug in new "polymorphic selectin" loading when a selection of
- polymorphic objects were to be partially loaded from a relationship
- lazy loader, leading to an "empty IN" condition within the load that
- raises an error for the "inline" form of "IN".
-
- .. change::
- :tags: bug, sql
- :tickets: 4204
-
- Fixed bug in CTE rendering where a :class:`_expression.CTE` that was also turned into
- an :class:`_expression.Alias` would not render its "ctename AS aliasname" clause
- appropriately if there were more than one reference to the CTE in a FROM
- clause.
-
- .. change::
- :tags: bug, orm
- :tickets: 4209
-
- Fixed 1.2 regression where a mapper option that contains an
- :class:`.AliasedClass` object, as is typical when using the
- :meth:`.QueryableAttribute.of_type` method, could not be pickled. 1.1's
- behavior was to omit the aliased class objects from the path, so this
- behavior is restored.
-
- .. change::
- :tags: feature, orm
- :versions: 1.3.0b1
-
- Added new feature :meth:`_query.Query.only_return_tuples`. Causes the
- :class:`_query.Query` object to return keyed tuple objects unconditionally even
- if the query is against a single entity. Pull request courtesy Eric
- Atkin.
-
-
- .. change::
- :tags: bug, sql
- :tickets: 4198
-
- Fixed bug in new "expanding IN parameter" feature where the bind parameter
- processors for values wasn't working at all, tests failed to cover this
- pretty basic case which includes that ENUM values weren't working.
-
-.. changelog::
- :version: 1.2.4
- :released: February 22, 2018
-
- .. change::
- :tags: bug, orm
- :tickets: 4193
-
- Fixed 1.2 regression in ORM versioning feature where a mapping against a
- :func:`_expression.select` or :func:`.alias` that also used a versioning column
- against the underlying table would fail due to the check added as part of
- :ticket:`3673`.
-
- .. change::
- :tags: bug, engine
- :tickets: 4190
-
- Fixed regression caused in 1.2.3 due to fix from :ticket:`4181` where
- the changes to the event system involving :class:`_engine.Engine` and
- :class:`.OptionEngine` did not accommodate for event removals, which
- would raise an ``AttributeError`` when invoked at the class
- level.
-
- .. change::
- :tags: bug, sql
- :tickets: 4197
-
- Fixed bug where CTE expressions would not have their name or alias name
- quoted when the given name is case sensitive or otherwise requires quoting.
- Pull request courtesy Eric Atkin.
-
-.. changelog::
- :version: 1.2.3
- :released: February 16, 2018
-
- .. change::
- :tags: bug, oracle
- :tickets: 4182
-
- Fixed bug in cx_Oracle disconnect detection, used by pre_ping and other
- features, where an error could be raised as DatabaseError which includes a
- numeric error code; previously we weren't checking in this case for a
- disconnect code.
-
- .. change::
- :tags: bug, sqlite
-
- Fixed the import error raised when a platform
- has neither pysqlite2 nor sqlite3 installed, such
- that the sqlite3-related import error is raised,
- not the pysqlite2 one which is not the actual
- failure mode. Pull request courtesy Robin.
-
- .. change::
- :tags: bug, orm
- :tickets: 4175
-
- Fixed bug where the :class:`.Bundle` object did not
- correctly report upon the primary :class:`_orm.Mapper` object
- represented by the bundle, if any. An immediate
- side effect of this issue was that the new selectinload
- loader strategy wouldn't work with the horizontal sharding
- extension.
-
- .. change::
- :tags: bug, sql
- :tickets: 4180
-
- Fixed bug where the :class:`.Enum` type wouldn't handle
- enum "aliases" correctly, when more than one key refers to the
- same value. Pull request courtesy Daniel Knell.
-
-
- .. change::
- :tags: bug, engine
- :tickets: 4181
-
- Fixed bug where events associated with an :class:`Engine`
- at the class level would be doubled when the
- :meth:`_engine.Engine.execution_options` method were used. To
- achieve this, the semi-private class :class:`.OptionEngine`
- no longer accepts events directly at the class level
- and will raise an error; the class only propagates class-level
- events from its parent :class:`_engine.Engine`. Instance-level
- events continue to work as before.
-
- .. change::
- :tags: bug, tests
- :tickets: 3265
-
- A test added in 1.2 thought to confirm a Python 2.7 behavior turns out to
- be confirming the behavior only as of Python 2.7.8. Python bug #8743 still
- impacts set comparison in Python 2.7.7 and earlier, so the test in question
- involving AssociationSet no longer runs for these older Python 2.7
- versions.
-
- .. change::
- :tags: feature, oracle
-
- The ON DELETE options for foreign keys are now part of
- Oracle reflection. Oracle does not support ON UPDATE
- cascades. Pull request courtesy Miroslav Shubernetskiy.
-
-
-
- .. change::
- :tags: bug, orm
- :tickets: 4188
-
- Fixed bug in concrete inheritance mapping where user-defined
- attributes such as hybrid properties that mirror the names
- of mapped attributes from sibling classes would be overwritten by
- the mapper as non-accessible at the instance level. Additionally
- ensured that user-bound descriptors are not implicitly invoked at the class
- level during the mapper configuration stage.
-
- .. change::
- :tags: bug, orm
- :tickets: 4178
-
- Fixed bug where the :func:`_orm.reconstructor` event
- helper would not be recognized if it were applied to the
- ``__init__()`` method of the mapped class.
-
- .. change::
- :tags: bug, engine
- :tickets: 4170
-
- The :class:`.URL` object now allows query keys to be specified multiple
- times where their values will be joined into a list. This is to support
- the plugins feature documented at :class:`.CreateEnginePlugin` which
- documents that "plugin" can be passed multiple times. Additionally, the
- plugin names can be passed to :func:`_sa.create_engine` outside of the URL
- using the new :paramref:`_sa.create_engine.plugins` parameter.
-
- .. change::
- :tags: feature, sql
- :tickets: 3906
-
- Added support for :class:`.Enum` to persist the values of the enumeration,
- rather than the keys, when using a Python pep-435 style enumerated object.
- The user supplies a callable function that will return the string values to
- be persisted. This allows enumerations against non-string values to be
- value-persistable as well. Pull request courtesy Jon Snyder.
-
- .. change::
- :tags: feature, orm
-
- Added new argument :paramref:`.attributes.set_attribute.inititator`
- to the :func:`.attributes.set_attribute` function, allowing an
- event token received from a listener function to be propagated
- to subsequent set events.
-
-.. changelog::
- :version: 1.2.2
- :released: January 24, 2018
-
- .. change::
- :tags: bug, mssql
- :tickets: 4164
-
- Added ODBC error code 10054 to the list of error
- codes that count as a disconnect for ODBC / MSSQL server.
-
-
- .. change::
- :tags: bug, orm
- :tickets: 4171
-
- Fixed 1.2 regression regarding new bulk_replace event
- where a backref would fail to remove an object from the
- previous owner when a bulk-assignment assigned the
- object to a new owner.
-
- .. change::
- :tags: bug, oracle
- :tickets: 4163
-
- The cx_Oracle dialect now calls setinputsizes() with cx_Oracle.NCHAR
- unconditionally when the NVARCHAR2 datatype, in SQLAlchemy corresponding
- to sqltypes.Unicode(), is in use. Per cx_Oracle's author this allows
- the correct conversions to occur within the Oracle client regardless
- of the setting for NLS_NCHAR_CHARACTERSET.
-
- .. change::
- :tags: bug, mysql
-
- Added more MySQL 8.0 reserved words to the MySQL dialect
- for quoting purposes. Pull request courtesy
- Riccardo Magliocchetti.
-
-.. changelog::
- :version: 1.2.1
- :released: January 15, 2018
-
- .. change::
- :tags: bug, orm
- :tickets: 4159
-
- Fixed regression where pickle format of a Load / _UnboundLoad object (e.g.
- loader options) changed and ``__setstate__()`` was raising an
- UnboundLocalError for an object received from the legacy format, even
- though an attempt was made to do so. tests are now added to ensure this
- works.
-
- .. change::
- :tags: bug, ext
- :tickets: 4150
-
- Fixed regression in association proxy due to :ticket:`3769`
- (allow for chained any() / has()) where contains() against
- an association proxy chained in the form
- (o2m relationship, associationproxy(m2o relationship, m2o relationship))
- would raise an error regarding the re-application of contains()
- on the final link of the chain.
-
- .. change::
- :tags: bug, orm
- :tickets: 4153
-
- Fixed regression caused by new lazyload caching scheme in :ticket:`3954`
- where a query that makes use of loader options with of_type would cause
- lazy loads of unrelated paths to fail with a TypeError.
-
- .. change::
- :tags: bug, oracle
- :tickets: 4157
-
- Fixed regression where the removal of most setinputsizes
- rules from cx_Oracle dialect impacted the TIMESTAMP
- datatype's ability to retrieve fractional seconds.
-
-
-
- .. change::
- :tags: bug, tests
-
- Removed an oracle-specific requirements rule from the public
- test suite that was interfering with third party dialect
- suites.
-
- .. change::
- :tags: bug, mssql
- :tickets: 4154
-
- Fixed regression in 1.2 where newly repaired quoting
- of collation names in :ticket:`3785` breaks SQL Server,
- which explicitly does not understand a quoted collation
- name. Whether or not mixed-case collation names are
- quoted or not is now deferred down to a dialect-level
- decision so that each dialect can prepare these identifiers
- directly.
-
- .. change::
- :tags: bug, orm
- :tickets: 4156
-
- Fixed bug in new "selectin" relationship loader where the loader could try
- to load a non-existent relationship when loading a collection of
- polymorphic objects, where only some of the mappers include that
- relationship, typically when :meth:`.PropComparator.of_type` is being used.
-
- .. change::
- :tags: bug, tests
-
- Added a new exclusion rule group_by_complex_expression
- which disables tests that use "GROUP BY <expr>", which seems
- to be not viable for at least two third party dialects.
-
- .. change::
- :tags: bug, oracle
-
- Fixed regression in Oracle imports where a missing comma caused
- an undefined symbol to be present. Pull request courtesy
- Miroslav Shubernetskiy.
-
-.. changelog::
- :version: 1.2.0
- :released: December 27, 2017
-
- .. change::
- :tags: orm, feature
- :tickets: 4137
-
- Added a new data member to the identity key tuple
- used by the ORM's identity map, known as the
- "identity_token". This token defaults to None but
- may be used by database sharding schemes to differentiate
- objects in memory with the same primary key that come
- from different databases. The horizontal sharding
- extension integrates this token applying the shard
- identifier to it, thus allowing primary keys to be
- duplicated across horizontally sharded backends.
-
- .. seealso::
-
- :ref:`change_4137`
-
- .. change::
- :tags: bug, mysql
- :tickets: 4115
-
- Fixed regression from issue 1.2.0b3 where "MariaDB" version comparison can
- fail for some particular MariaDB version strings under Python 3.
-
- .. change::
- :tags: enhancement, sql
- :tickets: 959
-
- Implemented "DELETE..FROM" syntax for PostgreSQL, MySQL, MS SQL Server
- (as well as within the unsupported Sybase dialect) in a manner similar
- to how "UPDATE..FROM" works. A DELETE statement that refers to more than
- one table will switch into "multi-table" mode and render the appropriate
- "USING" or multi-table "FROM" clause as understood by the database.
- Pull request courtesy Pieter Mulder.
-
- .. seealso::
-
- :ref:`change_959`
-
- .. change::
- :tags: bug, sql
- :tickets: 2694
-
- Reworked the new "autoescape" feature introduced in
- :ref:`change_2694` in 1.2.0b2 to be fully automatic; the escape
- character now defaults to a forwards slash ``"/"`` and
- is applied to percent, underscore, as well as the escape
- character itself, for fully automatic escaping. The
- character can also be changed using the "escape" parameter.
-
- .. seealso::
-
- :ref:`change_2694`
-
-
- .. change::
- :tags: bug, sql
- :tickets: 4147
-
- Fixed bug where the :meth:`_schema.Table.tometadata` method would not properly
- accommodate :class:`.Index` objects that didn't consist of simple
- column expressions, such as indexes against a :func:`_expression.text` construct,
- indexes that used SQL expressions or :attr:`.func`, etc. The routine
- now copies expressions fully to a new :class:`.Index` object while
- substituting all table-bound :class:`_schema.Column` objects for those
- of the target table.
-
- .. change::
- :tags: bug, sql
- :tickets: 4142
-
- Changed the "visit name" of :class:`_expression.ColumnElement` from "column" to
- "column_element", so that when this element is used as the basis for a
- user-defined SQL element, it is not assumed to behave like a table-bound
- :class:`.ColumnClause` when processed by various SQL traversal utilities,
- as are commonly used by the ORM.
-
- .. change::
- :tags: bug, sql, ext
- :tickets: 4141
-
- Fixed issue in :class:`_types.ARRAY` datatype which is essentially the same
- issue as that of :ticket:`3832`, except not a regression, where
- column attachment events on top of :class:`_types.ARRAY` would not fire
- correctly, thus interfering with systems which rely upon this. A key
- use case that was broken by this is the use of mixins to declare
- columns that make use of :meth:`.MutableList.as_mutable`.
-
- .. change::
- :tags: feature, engine
- :tickets: 4089
-
- The "password" attribute of the :class:`.url.URL` object can now be
- any user-defined or user-subclassed string object that responds to the
- Python ``str()`` builtin. The object passed will be maintained as the
- datamember :attr:`.url.URL.password_original` and will be consulted
- when the :attr:`.url.URL.password` attribute is read to produce the
- string value.
-
- .. change::
- :tags: bug, orm
- :tickets: 4130
-
- Fixed bug in :func:`.contains_eager` query option where making use of a
- path that used :meth:`.PropComparator.of_type` to refer to a subclass
- across more than one level of joins would also require that the "alias"
- argument were provided with the same subtype in order to avoid adding
- unwanted FROM clauses to the query; additionally, using
- :func:`.contains_eager` across subclasses that use :func:`.aliased` objects
- of subclasses as the :meth:`.PropComparator.of_type` argument will also
- render correctly.
-
-
-
-
- .. change::
- :tags: feature, postgresql
-
- Added new :class:`_postgresql.MONEY` datatype. Pull request courtesy
- Cleber J Santos.
-
- .. change::
- :tags: bug, sql
- :tickets: 4140
-
- Fixed bug in new "expanding bind parameter" feature whereby if multiple
- params were used in one statement, the regular expression would not
- match the parameter name correctly.
-
- .. change::
- :tags: enhancement, ext
- :tickets: 4135
-
- Added new method :meth:`.baked.Result.with_post_criteria` to baked
- query system, allowing non-SQL-modifying transformations to take place
- after the query has been pulled from the cache. Among other things,
- this method can be used with :class:`.horizontal_shard.ShardedQuery`
- to set the shard identifier. :class:`.horizontal_shard.ShardedQuery`
- has also been modified such that its :meth:`.ShardedQuery.get` method
- interacts correctly with that of :class:`_baked.Result`.
-
- .. change::
- :tags: bug, oracle
- :tickets: 4064
-
- Added some additional rules to fully handle ``Decimal('Infinity')``,
- ``Decimal('-Infinity')`` values with cx_Oracle numerics when using
- ``asdecimal=True``.
-
- .. change::
- :tags: bug, mssql
- :tickets: 4121
-
- Fixed bug where sqltypes.BINARY and sqltypes.VARBINARY datatypes
- would not include correct bound-value handlers for pyodbc,
- which allows the pyodbc.NullParam value to be passed that
- helps with FreeTDS.
-
-
-
-
- .. change::
- :tags: feature, misc
-
- Added a new errors section to the documentation with background
- about common error messages. Selected exceptions within SQLAlchemy
- will include a link in their string output to the relevant section
- within this page.
-
- .. change::
- :tags: bug, orm
- :tickets: 4032
-
- The :meth:`_query.Query.exists` method will now disable eager loaders for when
- the query is rendered. Previously, joined-eager load joins would be rendered
- unnecessarily as well as subquery eager load queries would be needlessly
- generated. The new behavior matches that of the :meth:`_query.Query.subquery`
- method.
-
-.. changelog::
- :version: 1.2.0b3
- :released: December 27, 2017
- :released: October 13, 2017
-
- .. change::
- :tags: feature, postgresql
- :tickets: 4109
-
- Added a new flag ``use_batch_mode`` to the psycopg2 dialect. This flag
- enables the use of psycopg2's ``psycopg2.extras.execute_batch``
- extension when the :class:`_engine.Engine` calls upon
- ``cursor.executemany()``. This extension provides a critical
- performance increase by over an order of magnitude when running INSERT
- statements in batch. The flag is False by default as it is considered
- to be experimental for now.
-
- .. seealso::
-
- :ref:`change_4109`
-
- .. change::
- :tags: bug, mssql
- :tickets: 4061
-
- SQL Server supports what SQLAlchemy calls "native boolean"
- with its BIT type, as this type only accepts 0 or 1 and the
- DBAPIs return its value as True/False. So the SQL Server
- dialects now enable "native boolean" support, in that a
- CHECK constraint is not generated for a :class:`.Boolean`
- datatype. The only difference vs. other native boolean
- is that there are no "true" / "false" constants so "1" and
- "0" are still rendered here.
-
-
- .. change::
- :tags: bug, oracle
- :tickets: 4064
-
- Partial support for persisting and retrieving the Oracle value
- "infinity" is implemented with cx_Oracle, using Python float values
- only, e.g. ``float("inf")``. Decimal support is not yet fulfilled by
- the cx_Oracle DBAPI driver.
-
- .. change::
- :tags: bug, oracle
-
- The cx_Oracle dialect has been reworked and modernized to take advantage of
- new patterns that weren't present in the old 4.x series of cx_Oracle. This
- includes that the minimum cx_Oracle version is the 5.x series and that
- cx_Oracle 6.x is now fully tested. The most significant change involves
- type conversions, primarily regarding the numeric / floating point and LOB
- datatypes, making more effective use of cx_Oracle type handling hooks to
- simplify how bind parameter and result data is processed.
-
- .. seealso::
-
- :ref:`change_cxoracle_12`
-
- .. change::
- :tags: bug, oracle
- :tickets: 3997
-
- two phase support for cx_Oracle has been completely removed for all
- versions of cx_Oracle, whereas in 1.2.0b1 this change only took effect for
- the 6.x series of cx_Oracle. This feature never worked correctly
- in any version of cx_Oracle and in cx_Oracle 6.x, the API which SQLAlchemy
- relied upon was removed.
-
- .. seealso::
-
- :ref:`change_cxoracle_12`
-
- .. change::
- :tags: bug, oracle
-
- The column keys present in a result set when using :meth:`_expression.Insert.returning`
- with the cx_Oracle backend now use the correct column / label names
- like that of all other dialects. Previously, these came out as
- ``ret_nnn``.
-
- .. seealso::
-
- :ref:`change_cxoracle_12`
-
- .. change::
- :tags: bug, oracle
-
- Several parameters to the cx_Oracle dialect are now deprecated and will
- have no effect: ``auto_setinputsizes``, ``exclude_setinputsizes``,
- ``allow_twophase``.
-
- .. seealso::
-
- :ref:`change_cxoracle_12`
-
-
- .. change::
- :tags: bug, sql
- :tickets: 4075
-
- Added a new method :meth:`.DefaultExecutionContext.get_current_parameters`
- which is used within a function-based default value generator in
- order to retrieve the current parameters being passed to the statement.
- The new function differs from the
- :attr:`.DefaultExecutionContext.current_parameters` attribute in
- that it also provides for optional grouping of parameters that
- correspond to a multi-valued "insert" construct. Previously it was not
- possible to identify the subset of parameters that were relevant to
- the function call.
-
- .. seealso::
-
- :ref:`change_4075`
-
- :ref:`context_default_functions`
-
- .. change::
- :tags: bug, orm
- :tickets: 4050
-
- Fixed regression introduced in 1.2.0b1 due to :ticket:`3934` where the
- :class:`.Session` would fail to "deactivate" the transaction, if a
- rollback failed (the target issue is when MySQL loses track of a SAVEPOINT).
- This would cause a subsequent call to :meth:`.Session.rollback` to raise
- an error a second time, rather than completing and bringing the
- :class:`.Session` back to ACTIVE.
-
- .. change::
- :tags: bug, postgresql
- :tickets: 4041
-
- Fixed bug where the pg8000 driver would fail if using
- :meth:`_schema.MetaData.reflect` with a schema name, since the schema name would
- be sent as a "quoted_name" object that's a string subclass, which pg8000
- doesn't recognize. The quoted_name type is added to pg8000's
- py_types collection on connect.
-
- .. change::
- :tags: bug, postgresql
- :tickets: 4016
-
- Enabled UUID support for the pg8000 driver, which supports native Python
- uuid round trips for this datatype. Arrays of UUID are still not supported,
- however.
-
- .. change::
- :tags: mssql, bug
- :tickets: 4057
-
- Fixed the pymssql dialect so that percent signs in SQL text, such
- as used in modulus expressions or literal textual values, are
- **not** doubled up, as seems to be what pymssql expects. This is
- despite the fact that the pymssql DBAPI uses the "pyformat" parameter
- style which itself considers the percent sign to be significant.
-
- .. change::
- :tags: bug, orm, declarative
- :tickets: 4091
-
- A warning is emitted if a subclass attempts to override an attribute
- that was declared on a superclass using ``@declared_attr.cascading``
- that the overridden attribute will be ignored. This use
- case cannot be fully supported down to further subclasses without more
- complex development efforts, so for consistency the "cascading" is
- honored all the way down regardless of overriding attributes.
-
- .. change::
- :tags: bug, orm, declarative
- :tickets: 4092
-
- A warning is emitted if the ``@declared_attr.cascading`` attribute is
- used with a special declarative name such as ``__tablename__``, as this
- has no effect.
-
- .. change::
- :tags: feature, engine
- :tickets: 4077
-
- Added ``__next__()`` and ``next()`` methods to :class:`_engine.ResultProxy`,
- so that the ``next()`` builtin function works on the object directly.
- :class:`_engine.ResultProxy` has long had an ``__iter__()`` method which already
- allows it to respond to the ``iter()`` builtin. The implementation
- for ``__iter__()`` is unchanged, as performance testing has indicated
- that iteration using a ``__next__()`` method with ``StopIteration``
- is about 20% slower in both Python 2.7 and 3.6.
-
- .. change::
- :tags: feature, mssql
- :tickets: 4086
-
- Added a new :class:`_mssql.TIMESTAMP` datatype, that
- correctly acts like a binary datatype for SQL Server
- rather than a datetime type, as SQL Server breaks the
- SQL standard here. Also added :class:`_mssql.ROWVERSION`,
- as the "TIMESTAMP" type in SQL Server is deprecated in
- favor of ROWVERSION.
-
- .. change::
- :tags: bug, orm
- :tickets: 4084
-
- Fixed issue where the :func:`.make_transient_to_detached` function
- would expire all attributes on the target object, including "deferred"
- attributes, which has the effect of the attribute being undeferred
- for the next refresh, causing an unexpected load of the attribute.
-
- .. change::
- :tags: bug, orm
- :tickets: 4040
-
- Fixed bug involving delete-orphan cascade where a related item
- that becomes an orphan before the parent object is part of a
- session is still tracked as moving into orphan status, which results
- in it being expunged from the session rather than being flushed.
-
- .. note:: This fix was inadvertently merged during the 1.2.0b3
- release and was **not added to the changelog** at that time.
- This changelog note was added to the release retroactively as of
- version 1.2.13.
-
- .. change::
- :tags: bug, orm
- :tickets: 4026
-
- Fixed bug in :ref:`change_3948` which prevented "selectin" and
- "inline" settings in a multi-level class hierarchy from interacting
- together as expected.
-
- .. change::
- :tags: bug, oracle
- :tickets: 4042
-
- Fixed bug where an index reflected under Oracle with an expression like
- "column DESC" would not be returned, if the table also had no primary
- key, as a result of logic that attempts to filter out the
- index implicitly added by Oracle onto the primary key columns.
-
- .. change::
- :tags: bug, orm
- :tickets: 4071
-
- Removed the warnings that are emitted when the LRU caches employed
- by the mapper as well as loader strategies reach their threshold; the
- purpose of this warning was at first a guard against excess cache keys
- being generated but became basically a check on the "creating many
- engines" antipattern. While this is still an antipattern, the presence
- of test suites which both create an engine per test as well as raise
- on all warnings will be an inconvenience; it should not be critical
- that such test suites change their architecture just for this warning
- (though engine-per-test suite is always better).
-
- .. change::
- :tags: bug, orm
- :tickets: 4049
-
- Fixed regression where the use of a :func:`.undefer_group` option
- in conjunction with a lazy loaded relationship option would cause
- an attribute error, due to a bug in the SQL cache key generation
- added in 1.2 as part of :ticket:`3954`.
-
- .. change::
- :tags: bug, oracle
- :tickets: 4045
-
- Fixed more regressions caused by cx_Oracle 6.0; at the moment, the only
- behavioral change for users is disconnect detection now detects for
- cx_Oracle.DatabaseError in addition to cx_Oracle.InterfaceError, as
- this behavior seems to have changed. Other issues regarding numeric
- precision and uncloseable connections are pending with the upstream
- cx_Oracle issue tracker.
-
- .. change::
- :tags: bug, mssql
- :tickets: 4060
-
- Fixed bug where the SQL Server dialect could pull columns from multiple
- schemas when reflecting a self-referential foreign key constraint, if
- multiple schemas contained a constraint of the same name against a
- table of the same name.
-
-
- .. change::
- :tags: feature, mssql
- :tickets: 4058
-
- Added support for "AUTOCOMMIT" isolation level, as established
- via :meth:`_engine.Connection.execution_options`, to the
- PyODBC and pymssql dialects. This isolation level sets the
- appropriate DBAPI-specific flags on the underlying
- connection object.
-
- .. change::
- :tags: bug, orm
- :tickets: 4073
-
- Modified the change made to the ORM update/delete evaluator in
- :ticket:`3366` such that if an unmapped column expression is present
- in the update or delete, if the evaluator can match its name to the
- mapped columns of the target class, a warning is emitted, rather than
- raising UnevaluatableError. This is essentially the pre-1.2 behavior,
- and is to allow migration for applications that are currently relying
- upon this pattern. However, if the given attribute name cannot be
- matched to the columns of the mapper, the UnevaluatableError is
- still raised, which is what was fixed in :ticket:`3366`.
-
- .. change::
- :tags: bug, sql
- :tickets: 4087
-
- Fixed bug in new SQL comments feature where table and column comment
- would not be copied when using :meth:`_schema.Table.tometadata`.
-
- .. change::
- :tags: bug, sql
- :tickets: 4102
-
- In release 1.1, the :class:`.Boolean` type was broken in that
- boolean coercion via ``bool()`` would occur for backends that did not
- feature "native boolean", but would not occur for native boolean backends,
- meaning the string ``"0"`` now behaved inconsistently. After a poll, a
- consensus was reached that non-boolean values should be raising an error,
- especially in the ambiguous case of string ``"0"``; so the :class:`.Boolean`
- datatype will now raise ``ValueError`` if an incoming value is not
- within the range ``None, True, False, 1, 0``.
-
- .. seealso::
-
- :ref:`change_4102`
-
- .. change::
- :tags: bug, sql
- :tickets: 4063
-
- Refined the behavior of :meth:`.Operators.op` such that in all cases,
- if the :paramref:`.Operators.op.is_comparison` flag is set to True,
- the return type of the resulting expression will be
- :class:`.Boolean`, and if the flag is False, the return type of the
- resulting expression will be the same type as that of the left-hand
- expression, which is the typical default behavior of other operators.
- Also added a new parameter :paramref:`.Operators.op.return_type` as well
- as a helper method :meth:`.Operators.bool_op`.
-
- .. seealso::
-
- :ref:`change_4063`
-
- .. change::
- :tags: bug, mysql
- :tickets: 4072
-
- Changed the name of the ``.values`` attribute of the new MySQL
- INSERT..ON DUPLICATE KEY UPDATE construct to ``.inserted``, as
- :class:`_expression.Insert` already has a method called :meth:`_expression.Insert.values`.
- The ``.inserted`` attribute ultimately renders the MySQL ``VALUES()``
- function.
-
- .. change::
- :tags: bug, mssql, orm
- :tickets: 4062
-
- Added a new class of "rowcount support" for dialects that is specific to
- when "RETURNING", which on SQL Server looks like "OUTPUT inserted", is in
- use, as the PyODBC backend isn't able to give us rowcount on an UPDATE or
- DELETE statement when OUTPUT is in effect. This primarily affects the ORM
- when a flush is updating a row that contains server-calculated values,
- raising an error if the backend does not return the expected row count.
- PyODBC now states that it supports rowcount except if OUTPUT.inserted is
- present, which is taken into account by the ORM during a flush as to
- whether it will look for a rowcount.
-
- .. change::
- :tags: bug, sql
- :tickets: 4088
-
- Internal refinements to the :class:`.Enum`, :class:`.Interval`, and
- :class:`.Boolean` types, which now extend a common mixin
- :class:`.Emulated` that indicates a type that provides Python-side
- emulation of a DB native type, switching out to the DB native type when
- a supporting backend is in use. The PostgreSQL
- :class:`_postgresql.INTERVAL` type when used directly will now include
- the correct type coercion rules for SQL expressions that also take
- effect for :class:`_types.Interval` (such as adding a date to an
- interval yields a datetime).
-
-
- .. change::
- :tags: bug, mssql, orm
-
- Enabled the "sane_rowcount" flag for the pymssql dialect, indicating
- that the DBAPI now reports the correct number of rows affected from
- an UPDATE or DELETE statement. This impacts mostly the ORM versioning
- feature in that it now can verify the number of rows affected on a
- target version.
-
- .. change:: 4028
- :tags: bug, engine
- :tickets: 4028
-
- Made some adjustments to :class:`_pool.Pool` and :class:`_engine.Connection` such
- that recovery logic is not run underneath exception catches for
- ``pool.Empty``, ``AttributeError``, since when the recovery operation
- itself fails, Python 3 creates a misleading stack trace referring to the
- ``Empty`` / ``AttributeError`` as the cause, when in fact these exception
- catches are part of control flow.
-
-
- .. change::
- :tags: bug, oracle
- :tickets: 4076
-
- Fixed bug where Oracle 8 "non ansi" join mode would not add the
- ``(+)`` operator to expressions that used an operator other than the
- ``=`` operator. The ``(+)`` needs to be on all columns that are part
- of the right-hand side.
-
- .. change::
- :tags: bug, mssql
- :tickets: 4059
-
- Added a rule to SQL Server index reflection to ignore the so-called
- "heap" index that is implicitly present on a table that does not
- specify a clustered index.
-
-
-.. changelog::
- :version: 1.2.0b2
- :released: December 27, 2017
- :released: July 24, 2017
-
- .. change:: 4033
- :tags: bug, orm
- :tickets: 4033
-
- Fixed regression from 1.1.11 where adding additional non-entity
- columns to a query that includes an entity with subqueryload
- relationships would fail, due to an inspection added in 1.1.11 as a
- result of :ticket:`4011`.
-
-
-.. changelog::
- :version: 1.2.0b1
- :released: December 27, 2017
- :released: July 10, 2017
-
- .. change:: scoped_autocommit
- :tags: feature, orm
-
- Added ``.autocommit`` attribute to :class:`.scoped_session`, proxying
- the ``.autocommit`` attribute of the underling :class:`.Session`
- currently assigned to the thread. Pull request courtesy
- Ben Fagin.
-
- .. change:: 4009
- :tags: feature, mysql
- :tickets: 4009
-
- Added support for MySQL's ON DUPLICATE KEY UPDATE
- MySQL-specific :class:`.mysql.dml.Insert` object.
- Pull request courtesy Michael Doronin.
-
- .. seealso::
-
- :ref:`change_4009`
-
- .. change:: 4018
- :tags: bug, sql
- :tickets: 4018
-
- The rules for type coercion between :class:`.Numeric`, :class:`.Integer`,
- and date-related types now include additional logic that will attempt
- to preserve the settings of the incoming type on the "resolved" type.
- Currently the target for this is the ``asdecimal`` flag, so that
- a math operation between :class:`.Numeric` or :class:`.Float` and
- :class:`.Integer` will preserve the "asdecimal" flag as well as
- if the type should be the :class:`.Float` subclass.
-
- .. seealso::
-
- :ref:`change_floats_12`
-
- .. change:: 4020
- :tags: bug, sql, mysql
- :tickets: 4020
-
- The result processor for the :class:`.Float` type now unconditionally
- runs values through the ``float()`` processor if the dialect
- specifies that it also supports "native decimal" mode. While most
- backends will deliver Python ``float`` objects for a floating point
- datatype, the MySQL backends in some cases lack the typing information
- in order to provide this and return ``Decimal`` unless the float
- conversion is done.
-
- .. seealso::
-
- :ref:`change_floats_12`
-
- .. change:: 4017
- :tags: bug, sql
- :tickets: 4017
-
- Added some extra strictness to the handling of Python "float" values
- passed to SQL statements. A "float" value will be associated with the
- :class:`.Float` datatype and not the Decimal-coercing :class:`.Numeric`
- datatype as was the case before, eliminating a confusing warning
- emitted on SQLite as well as unnecessary coercion to Decimal.
-
- .. seealso::
-
- :ref:`change_floats_12`
-
- .. change:: 3058
- :tags: feature, orm
- :tickets: 3058
-
- Added a new feature :func:`_orm.with_expression` that allows an ad-hoc
- SQL expression to be added to a specific entity in a query at result
- time. This is an alternative to the SQL expression being delivered as
- a separate element in the result tuple.
-
- .. seealso::
-
- :ref:`change_3058`
-
- .. change:: 3496
- :tags: bug, orm
- :tickets: 3496
-
- An UPDATE emitted as a result of the
- :paramref:`_orm.relationship.post_update` feature will now integrate with
- the versioning feature to both bump the version id of the row as well
- as assert that the existing version number was matched.
-
- .. seealso::
-
- :ref:`change_3496`
-
- .. change:: 3769
- :tags: bug, ext
- :tickets: 3769
-
- The :meth:`.AssociationProxy.any`, :meth:`.AssociationProxy.has`
- and :meth:`.AssociationProxy.contains` comparison methods now support
- linkage to an attribute that is itself also an
- :class:`.AssociationProxy`, recursively.
-
- .. seealso::
-
- :ref:`change_3769`
-
- .. change:: 3853
- :tags: bug, ext
- :tickets: 3853
-
- Implemented in-place mutation operators ``__ior__``, ``__iand__``,
- ``__ixor__`` and ``__isub__`` for :class:`.mutable.MutableSet`
- and ``__iadd__`` for :class:`.mutable.MutableList` so that change
- events are fired off when these mutator methods are used to alter the
- collection.
-
- .. seealso::
-
- :ref:`change_3853`
-
- .. change:: 3847
- :tags: bug, declarative
- :tickets: 3847
-
- A warning is emitted if the :attr:`.declared_attr.cascading` modifier
- is used with a declarative attribute that is itself declared on
- a class that is to be mapped, as opposed to a declarative mixin
- class or ``__abstract__`` class. The :attr:`.declared_attr.cascading`
- modifier currently only applies to mixin/abstract classes.
-
- .. change:: 4003
- :tags: feature, oracle
- :tickets: 4003
-
- The Oracle dialect now inspects unique and check constraints when using
- :meth:`_reflection.Inspector.get_unique_constraints`,
- :meth:`_reflection.Inspector.get_check_constraints`.
- As Oracle does not have unique constraints that are separate from a unique
- :class:`.Index`, a :class:`_schema.Table` that's reflected will still continue
- to not have :class:`.UniqueConstraint` objects associated with it.
- Pull requests courtesy Eloy Felix.
-
- .. seealso::
-
- :ref:`change_4003`
-
- .. change:: 3948
- :tags: feature, orm
- :tickets: 3948
-
- Added a new style of mapper-level inheritance loading
- "polymorphic selectin". This style of loading
- emits queries for each subclass in an inheritance
- hierarchy subsequent to the load of the base
- object type, using IN to specify the desired
- primary key values.
-
- .. seealso::
-
- :ref:`change_3948`
-
- .. change:: 3472
- :tags: bug, orm
- :tickets: 3471, 3472
-
- Repaired several use cases involving the
- :paramref:`_orm.relationship.post_update` feature when used in conjunction
- with a column that has an "onupdate" value. When the UPDATE emits,
- the corresponding object attribute is now expired or refreshed so that
- the newly generated "onupdate" value can populate on the object;
- previously the stale value would remain. Additionally, if the target
- attribute is set in Python for the INSERT of the object, the value is
- now re-sent during the UPDATE so that the "onupdate" does not overwrite
- it (note this works just as well for server-generated onupdates).
- Finally, the :meth:`.SessionEvents.refresh_flush` event is now emitted
- for these attributes when refreshed within the flush.
-
- .. seealso::
-
- :ref:`change_3471`
-
- .. change:: 3996
- :tags: bug, orm
- :tickets: 3996
-
- Fixed bug where programmatic version_id counter in conjunction with
- joined table inheritance would fail if the version_id counter
- were not actually incremented and no other values on the base table
- were modified, as the UPDATE would have an empty SET clause. Since
- programmatic version_id where version counter is not incremented
- is a documented use case, this specific condition is now detected
- and the UPDATE now sets the version_id value to itself, so that
- concurrency checks still take place.
-
- .. change:: 3848
- :tags: bug, orm, declarative
- :tickets: 3848
-
- Fixed bug where using :class:`.declared_attr` on an
- :class:`.AbstractConcreteBase` where a particular return value were some
- non-mapped symbol, including ``None``, would cause the attribute
- to hard-evaluate just once and store the value to the object
- dictionary, not allowing it to invoke for subclasses. This behavior
- is normal when :class:`.declared_attr` is on a mapped class, and
- does not occur on a mixin or abstract class. Since
- :class:`.AbstractConcreteBase` is both "abstract" and actually
- "mapped", a special exception case is made here so that the
- "abstract" behavior takes precedence for :class:`.declared_attr`.
-
- .. change:: 3673
- :tags: bug, orm
- :tickets: 3673
-
- The versioning feature does not support NULL for the version counter.
- An exception is now raised if the version id is programmatic and
- was set to NULL for an UPDATE. Pull request courtesy Diana Clarke.
-
- .. change:: 3999
- :tags: bug, sql
- :tickets: 3999
-
- The operator precedence for all comparison operators such as LIKE, IS,
- IN, MATCH, equals, greater than, less than, etc. has all been merged
- into one level, so that expressions which make use of these against
- each other will produce parentheses between them. This suits the
- stated operator precedence of databases like Oracle, MySQL and others
- which place all of these operators as equal precedence, as well as
- PostgreSQL as of 9.5 which has also flattened its operator precedence.
-
- .. seealso::
-
- :ref:`change_3999`
-
-
- .. change:: 3796
- :tags: bug, orm
- :tickets: 3796
-
- Removed a very old keyword argument from :class:`.scoped_session`
- called ``scope``. This keyword was never documented and was an
- early attempt at allowing for variable scopes.
-
- .. seealso::
-
- :ref:`change_3796`
-
- .. change:: 3871
- :tags: bug, mysql
- :tickets: 3871
-
- Added support for views that are unreflectable due to stale
- table definitions, when calling :meth:`_schema.MetaData.reflect`; a warning
- is emitted for the table that cannot respond to ``DESCRIBE``,
- but the operation succeeds.
-
- .. change:: baked_opts
- :tags: feature, ext
-
- Added new flag :paramref:`.Session.enable_baked_queries` to the
- :class:`.Session` to allow baked queries to be disabled
- session-wide, reducing memory use. Also added new :class:`.Bakery`
- wrapper so that the bakery returned by :paramref:`.BakedQuery.bakery`
- can be inspected.
-
- .. change:: 3988
- :tags: bug, orm
- :tickets: 3988
-
- Fixed bug where combining a "with_polymorphic" load in conjunction
- with subclass-linked relationships that specify joinedload with
- innerjoin=True, would fail to demote those "innerjoins" to
- "outerjoins" to suit the other polymorphic classes that don't
- support that relationship. This applies to both a single and a
- joined inheritance polymorphic load.
-
- .. change:: 3991
- :tags: bug, orm
- :tickets: 3991
-
- Added new argument :paramref:`.with_for_update` to the
- :meth:`.Session.refresh` method. When the :meth:`_query.Query.with_lockmode`
- method were deprecated in favor of :meth:`_query.Query.with_for_update`,
- the :meth:`.Session.refresh` method was never updated to reflect
- the new option.
-
- .. seealso::
-
- :ref:`change_3991`
-
- .. change:: 3984
- :tags: bug, orm
- :tickets: 3984
-
- Fixed bug where a :func:`.column_property` that is also marked as
- "deferred" would be marked as "expired" during a flush, causing it
- to be loaded along with the unexpiry of regular attributes even
- though this attribute was never accessed.
-
- .. change:: 3873
- :tags: bug, sql
- :tickets: 3873
-
- Repaired issue where the type of an expression that used
- :meth:`.ColumnOperators.is_` or similar would not be a "boolean" type,
- instead the type would be "nulltype", as well as when using custom
- comparison operators against an untyped expression. This typing can
- impact how the expression behaves in larger contexts as well as
- in result-row-handling.
-
- .. change:: 3941
- :tags: bug, ext
- :tickets: 3941
-
- Improved the association proxy list collection so that premature
- autoflush against a newly created association object can be prevented
- in the case where ``list.append()`` is being used, and a lazy load
- would be invoked when the association proxy accesses the endpoint
- collection. The endpoint collection is now accessed first before
- the creator is invoked to produce the association object.
-
- .. change:: 3969
- :tags: bug, sql
- :tickets: 3969
-
- Fixed the negation of a :class:`.Label` construct so that the
- inner element is negated correctly, when the :func:`.not_` modifier
- is applied to the labeled expression.
-
- .. change:: 3944
- :tags: feature, orm
- :tickets: 3944
-
- Added a new kind of eager loading called "selectin" loading. This
- style of loading is very similar to "subquery" eager loading,
- except that it uses an IN expression given a list of primary key
- values from the loaded parent objects, rather than re-stating the
- original query. This produces a more efficient query that is
- "baked" (e.g. the SQL string is cached) and also works in the
- context of :meth:`_query.Query.yield_per`.
-
- .. seealso::
-
- :ref:`change_3944`
-
- .. change::
- :tags: bug, orm
- :tickets: 3967
-
- Fixed bug in subquery eager loading where the "join_depth" parameter
- for self-referential relationships would not be correctly honored,
- loading all available levels deep rather than correctly counting
- the specified number of levels for eager loading.
-
- .. change::
- :tags: bug, orm
-
- Added warnings to the LRU "compiled cache" used by the :class:`_orm.Mapper`
- (and ultimately will be for other ORM-based LRU caches) such that
- when the cache starts hitting its size limits, the application will
- emit a warning that this is a performance-degrading situation that
- may require attention. The LRU caches can reach their size limits
- primarily if an application is making use of an unbounded number
- of :class:`_engine.Engine` objects, which is an antipattern. Otherwise,
- this may suggest an issue that should be brought to the SQLAlchemy
- developer's attention.
-
- .. change:: 3964
- :tags: bug, postgresql
- :tickets: 3964
-
- Fixed bug where the base :class:`_types.ARRAY` datatype would not
- invoke the bind/result processors of :class:`_postgresql.ARRAY`.
-
- .. change:: 3963
- :tags: bug, orm
- :tickets: 3963
-
- Fixed bug to improve upon the specificity of loader options that
- take effect subsequent to the lazy load of a related entity, so
- that the loader options will match to an aliased or non-aliased
- entity more specifically if those options include entity information.
-
- .. change:: 3954
- :tags: feature, orm
- :tickets: 3954
-
- The ``lazy="select"`` loader strategy now makes used of the
- :class:`.BakedQuery` query caching system in all cases. This
- removes most overhead of generating a :class:`_query.Query` object and
- running it into a :func:`_expression.select` and then string SQL statement from
- the process of lazy-loading related collections and objects. The
- "baked" lazy loader has also been improved such that it can now
- cache in most cases where query load options are used.
-
- .. seealso::
-
- :ref:`change_3954`
-
- .. change:: 3740
- :tags: bug, sql
- :tickets: 3740
-
- The system by which percent signs in SQL statements are "doubled"
- for escaping purposes has been refined. The "doubling" of percent
- signs mostly associated with the :obj:`_expression.literal_column` construct
- as well as operators like :meth:`.ColumnOperators.contains` now
- occurs based on the stated paramstyle of the DBAPI in use; for
- percent-sensitive paramstyles as are common with the PostgreSQL
- and MySQL drivers the doubling will occur, for others like that
- of SQLite it will not. This allows more database-agnostic use
- of the :obj:`_expression.literal_column` construct to be possible.
-
- .. seealso::
-
- :ref:`change_3740`
-
- .. change:: 3959
- :tags: bug, postgresql
- :tickets: 3959
-
- Added support for all possible "fields" identifiers when reflecting the
- PostgreSQL ``INTERVAL`` datatype, e.g. "YEAR", "MONTH", "DAY TO
- MINUTE", etc.. In addition, the :class:`_postgresql.INTERVAL`
- datatype itself now includes a new parameter
- :paramref:`.postgresql.INTERVAL.fields` where these qualifiers can be
- specified; the qualifier is also reflected back into the resulting
- datatype upon reflection / inspection.
-
- .. seealso::
-
- :ref:`change_3959`
-
- .. change:: 3957
- :tags: bug, sql
- :tickets: 3957
-
- Fixed bug where a column-level :class:`.CheckConstraint` would fail
- to compile the SQL expression using the underlying dialect compiler
- as well as apply proper flags to generate literal values as
- inline, in the case that the sqltext is a Core expression and
- not just a plain string. This was long-ago fixed for table-level
- check constraints in 0.9 as part of :ticket:`2742`, which more commonly
- feature Core SQL expressions as opposed to plain string expressions.
-
- .. change:: 2626
- :tags: bug, mssql
- :tickets: 2626
-
- The SQL Server dialect now allows for a database and/or owner name
- with a dot inside of it, using brackets explicitly in the string around
- the owner and optionally the database name as well. In addition,
- sending the :class:`.quoted_name` construct for the schema name will
- not split on the dot and will deliver the full string as the "owner".
- :class:`.quoted_name` is also now available from the ``sqlalchemy.sql``
- import space.
-
- .. seealso::
-
- :ref:`change_2626`
-
- .. change:: 3953
- :tags: feature, sql
- :tickets: 3953
-
- Added a new kind of :func:`.bindparam` called "expanding". This is
- for use in ``IN`` expressions where the list of elements is rendered
- into individual bound parameters at statement execution time, rather
- than at statement compilation time. This allows both a single bound
- parameter name to be linked to an IN expression of multiple elements,
- as well as allows query caching to be used with IN expressions. The
- new feature allows the related features of "select in" loading and
- "polymorphic in" loading to make use of the baked query extension
- to reduce call overhead. This feature should be considered to be
- **experimental** for 1.2.
-
- .. seealso::
-
- :ref:`change_3953`
-
- .. change:: 3923
- :tags: bug, sql
- :tickets: 3923
-
- Fixed bug where a SQL-oriented Python-side column default could fail to
- be executed properly upon INSERT in the "pre-execute" codepath, if the
- SQL itself were an untyped expression, such as plain text. The "pre-
- execute" codepath is fairly uncommon however can apply to non-integer
- primary key columns with SQL defaults when RETURNING is not used.
-
- .. change:: 3785
- :tags: bug, sql
- :tickets: 3785
-
- The expression used for COLLATE as rendered by the column-level
- :func:`_expression.collate` and :meth:`.ColumnOperators.collate` is now
- quoted as an identifier when the name is case sensitive, e.g. has
- uppercase characters. Note that this does not impact type-level
- collation, which is already quoted.
-
- .. seealso::
-
- :ref:`change_3785`
-
- .. change:: 3229
- :tags: feature, orm, ext
- :tickets: 3229
-
- The :meth:`_query.Query.update` method can now accommodate both
- hybrid attributes as well as composite attributes as a source
- of the key to be placed in the SET clause. For hybrids, an
- additional decorator :meth:`.hybrid_property.update_expression`
- is supplied for which the user supplies a tuple-returning function.
-
- .. seealso::
-
- :ref:`change_3229`
-
- .. change:: 3753
- :tags: bug, orm
- :tickets: 3753
-
- The :func:`.attributes.flag_modified` function now raises
- :class:`.InvalidRequestError` if the named attribute key is not
- present within the object, as this is assumed to be present
- in the flush process. To mark an object "dirty" for a flush
- without referring to any specific attribute, the
- :func:`.attributes.flag_dirty` function may be used.
-
- .. seealso::
-
- :ref:`change_3753`
-
- .. change:: 3911_3912
- :tags: bug, ext
- :tickets: 3911, 3912
-
- The :class:`sqlalchemy.ext.hybrid.hybrid_property` class now supports
- calling mutators like ``@setter``, ``@expression`` etc. multiple times
- across subclasses, and now provides a ``@getter`` mutator, so that
- a particular hybrid can be repurposed across subclasses or other
- classes. This now matches the behavior of ``@property`` in standard
- Python.
-
- .. seealso::
-
- :ref:`change_3911_3912`
-
-
-
- .. change:: 1546
- :tags: feature, sql, postgresql, mysql, oracle
- :tickets: 1546
-
- Added support for SQL comments on :class:`_schema.Table` and :class:`_schema.Column`
- objects, via the new :paramref:`_schema.Table.comment` and
- :paramref:`_schema.Column.comment` arguments. The comments are included
- as part of DDL on table creation, either inline or via an appropriate
- ALTER statement, and are also reflected back within table reflection,
- as well as via the :class:`_reflection.Inspector`. Supported backends currently
- include MySQL, PostgreSQL, and Oracle. Many thanks to Frazer McLean
- for a large amount of effort on this.
-
- .. seealso::
-
- :ref:`change_1546`
-
- .. change:: 3919
- :tags: feature, engine
- :tickets: 3919
-
- Added native "pessimistic disconnection" handling to the :class:`_pool.Pool`
- object. The new parameter :paramref:`_pool.Pool.pre_ping`, available from
- the engine as :paramref:`_sa.create_engine.pool_pre_ping`, applies an
- efficient form of the "pre-ping" recipe featured in the pooling
- documentation, which upon each connection check out, emits a simple
- statement, typically "SELECT 1", to test the connection for liveness.
- If the existing connection is no longer able to respond to commands,
- the connection is transparently recycled, and all other connections
- made prior to the current timestamp are invalidated.
-
- .. seealso::
-
- :ref:`pool_disconnects_pessimistic`
-
- :ref:`change_3919`
-
- .. change:: 3939
- :tags: bug, sql
- :tickets: 3939
-
- Fixed bug where the use of an :class:`_expression.Alias` object in a column
- context would raise an argument error when it tried to group itself
- into a parenthesized expression. Using :class:`_expression.Alias` in this way
- is not yet a fully supported API, however it applies to some end-user
- recipes and may have a more prominent role in support of some
- future PostgreSQL features.
-
- .. change:: 3366
- :tags: bug, orm
- :tickets: 3366
-
- The "evaluate" strategy used by :meth:`_query.Query.update` and
- :meth:`_query.Query.delete` can now accommodate a simple
- object comparison from a many-to-one relationship to an instance,
- when the attribute names of the primary key / foreign key columns
- don't match the actual names of the columns. Previously this would
- do a simple name-based match and fail with an AttributeError.
-
- .. change:: 3896_a
- :tags: feature, orm
- :tickets: 3896
-
- Added new attribute event :meth:`.AttributeEvents.bulk_replace`.
- This event is triggered when a collection is assigned to a
- relationship, before the incoming collection is compared with the
- existing one. This early event allows for conversion of incoming
- non-ORM objects as well. The event is integrated with the
- ``@validates`` decorator.
-
- .. seealso::
-
- :ref:`change_3896_event`
-
- .. change:: 3896_b
- :tags: bug, orm
- :tickets: 3896
-
- The ``@validates`` decorator now allows the decorated method to receive
- objects from a "bulk collection set" operation that have not yet
- been compared to the existing collection. This allows incoming values
- to be converted to compatible ORM objects as is already allowed
- from an "append" event. Note that this means that the
- ``@validates`` method is called for **all** values during a collection
- assignment, rather than just the ones that are new.
-
- .. seealso::
-
- :ref:`change_3896_validates`
-
- .. change:: 3938
- :tags: bug, engine
- :tickets: 3938
-
- Fixed bug where in the unusual case of passing a
- :class:`.Compiled` object directly to :meth:`_engine.Connection.execute`,
- the dialect with which the :class:`.Compiled` object were generated
- was not consulted for the paramstyle of the string statement, instead
- assuming it would match the dialect-level paramstyle, causing
- mismatches to occur.
-
- .. change:: 3303
- :tags: feature, orm
- :tickets: 3303
-
- Added new event handler :meth:`.AttributeEvents.modified` which is
- triggered when the func:`.attributes.flag_modified` function is
- invoked, which is common when using the :mod:`sqlalchemy.ext.mutable`
- extension module.
-
- .. seealso::
-
- :ref:`change_3303`
-
- .. change:: 3918
- :tags: bug, ext
- :tickets: 3918
-
- Fixed a bug in the ``sqlalchemy.ext.serializer`` extension whereby
- an "annotated" SQL element (as produced by the ORM for many types
- of SQL expressions) could not be reliably serialized. Also bumped
- the default pickle level for the serializer to "HIGHEST_PROTOCOL".
-
- .. change:: 3891
- :tags: bug, orm
- :tickets: 3891
-
- Fixed bug in single-table inheritance where the select_from()
- argument would not be taken into account when limiting rows
- to a subclass. Previously, only expressions in the
- columns requested would be taken into account.
-
- .. seealso::
-
- :ref:`change_3891`
-
- .. change:: 3913
- :tags: bug, orm
- :tickets: 3913
-
- When assigning a collection to an attribute mapped by a relationship,
- the previous collection is no longer mutated. Previously, the old
- collection would be emptied out in conjunction with the "item remove"
- events that fire off; the events now fire off without affecting
- the old collection.
-
- .. seealso::
-
- :ref:`change_3913`
-
- .. change:: 3932
- :tags: bug, oracle
- :tickets: 3932
-
- The cx_Oracle dialect now supports "sane multi rowcount", that is,
- when a series of parameter sets are executed via DBAPI
- ``cursor.executemany()``, we can make use of ``cursor.rowcount`` to
- verify the number of rows matched. This has an impact within the
- ORM when detecting concurrent modification scenarios, in that
- some simple conditions can now be detected even when the ORM
- is batching statements, as well as when the more strict versioning
- feature is used, the ORM can still use statement batching. The
- flag is enabled for cx_Oracle assuming at least version 5.0, which
- is now commonplace.
-
- .. change:: 3907
- :tags: feature, sql
- :tickets: 3907
-
- The longstanding behavior of the :meth:`.ColumnOperators.in_` and
- :meth:`.ColumnOperators.notin_` operators emitting a warning when
- the right-hand condition is an empty sequence has been revised;
- a simple "static" expression of "1 != 1" or "1 = 1" is now rendered
- by default, rather than pulling in the original left-hand
- expression. This causes the result for a NULL column comparison
- against an empty set to change from NULL to true/false. The
- behavior is configurable, and the old behavior can be enabled
- using the :paramref:`_sa.create_engine.empty_in_strategy` parameter
- to :func:`_sa.create_engine`.
-
- .. seealso::
-
- :ref:`change_3907`
-
- .. change:: 3276
- :tags: bug, oracle
- :tickets: 3276
-
- Oracle reflection now "normalizes" the name given to a foreign key
- constraint, that is, returns it as all lower case for a case
- insensitive name. This was already the behavior for indexes
- and primary key constraints as well as all table and column names.
- This will allow Alembic autogenerate scripts to compare and render
- foreign key constraint names correctly when initially specified
- as case insensitive.
-
- .. seealso::
-
- :ref:`change_3276`
-
- .. change:: 2694
- :tags: feature, sql
- :tickets: 2694
-
- Added a new option ``autoescape`` to the "startswith" and
- "endswith" classes of comparators; this supplies an escape character
- also applies it to all occurrences of the wildcard characters "%"
- and "_" automatically. Pull request courtesy Diana Clarke.
-
- .. note:: This feature has been changed as of 1.2.0 from its initial
- implementation in 1.2.0b2 such that autoescape is now passed as a
- boolean value, rather than a specific character to use as the escape
- character.
-
- .. seealso::
-
- :ref:`change_2694`
-
- .. change:: 3934
- :tags: bug, orm
- :tickets: 3934
-
- The state of the :class:`.Session` is now present when the
- :meth:`.SessionEvents.after_rollback` event is emitted, that is, the
- attribute state of objects prior to their being expired. This is now
- consistent with the behavior of the
- :meth:`.SessionEvents.after_commit` event which also emits before the
- attribute state of objects is expired.
-
- .. seealso::
-
- :ref:`change_3934`
-
- .. change:: 3607
- :tags: bug, orm
- :tickets: 3607
-
- Fixed bug where :meth:`_query.Query.with_parent` would not work if the
- :class:`_query.Query` were against an :func:`.aliased` construct rather than
- a regular mapped class. Also adds a new parameter
- :paramref:`.util.with_parent.from_entity` to the standalone
- :func:`.util.with_parent` function as well as
- :meth:`_query.Query.with_parent`.
+++ /dev/null
-=============
-1.3 Changelog
-=============
-
-.. changelog_imports::
-
- .. include:: changelog_12.rst
- :start-line: 5
-
- .. include:: changelog_11.rst
- :start-line: 5
-
-.. changelog::
- :version: 1.3.25
- :include_notes_from: unreleased_13
-
-.. changelog::
- :version: 1.3.24
- :released: March 30, 2021
-
- .. change::
- :tags: bug, schema
- :tickets: 6152
-
- Fixed bug first introduced in as some combination of :ticket:`2892`,
- :ticket:`2919` nnd :ticket:`3832` where the attachment events for a
- :class:`_types.TypeDecorator` would be doubled up against the "impl" class,
- if the "impl" were also a :class:`_types.SchemaType`. The real-world case
- is any :class:`_types.TypeDecorator` against :class:`_types.Enum` or
- :class:`_types.Boolean` would get a doubled
- :class:`_schema.CheckConstraint` when the ``create_constraint=True`` flag
- is set.
-
-
- .. change::
- :tags: bug, schema, sqlite
- :tickets: 6007
- :versions: 1.4.0
-
- Fixed issue where the CHECK constraint generated by :class:`_types.Boolean`
- or :class:`_types.Enum` would fail to render the naming convention
- correctly after the first compilation, due to an unintended change of state
- within the name given to the constraint. This issue was first introduced in
- 0.9 in the fix for issue #3067, and the fix revises the approach taken at
- that time which appears to have been more involved than what was needed.
-
- .. change::
- :tags: bug, orm
- :tickets: 5983
- :versions: 1.4.0
-
- Removed very old warning that states that passive_deletes is not intended
- for many-to-one relationships. While it is likely that in many cases
- placing this parameter on a many-to-one relationship is not what was
- intended, there are use cases where delete cascade may want to be
- disallowed following from such a relationship.
-
-
-
- .. change::
- :tags: bug, postgresql
- :tickets: 5989
- :versions: 1.4.0
-
- Fixed issue where using :class:`_postgresql.aggregate_order_by` would
- return ARRAY(NullType) under certain conditions, interfering with
- the ability of the result object to return data correctly.
-
- .. change::
- :tags: bug, schema
- :tickets: 5919
- :versions: 1.4.0
-
- Repaired / implemented support for primary key constraint naming
- conventions that use column names/keys/etc as part of the convention. In
- particular, this includes that the :class:`.PrimaryKeyConstraint` object
- that's automatically associated with a :class:`.schema.Table` will update
- its name as new primary key :class:`_schema.Column` objects are added to
- the table and then to the constraint. Internal failure modes related to
- this constraint construction process including no columns present, no name
- present or blank name present are now accommodated.
-
- .. change::
- :tags: bug, schema
- :tickets: 6071
- :versions: 1.4.3
-
- Adjusted the logic that emits DROP statements for :class:`_schema.Sequence`
- objects among the dropping of multiple tables, such that all
- :class:`_schema.Sequence` objects are dropped after all tables, even if the
- given :class:`_schema.Sequence` is related only to a :class:`_schema.Table`
- object and not directly to the overall :class:`_schema.MetaData` object.
- The use case supports the same :class:`_schema.Sequence` being associated
- with more than one :class:`_schema.Table` at a time.
-
- .. change::
- :tags: bug, orm
- :tickets: 5952
- :versions: 1.4.0
-
- Fixed issue where the process of joining two tables could fail if one of
- the tables had an unrelated, unresolvable foreign key constraint which
- would raise :class:`_exc.NoReferenceError` within the join process, which
- nonetheless could be bypassed to allow the join to complete. The logic
- which tested the exception for significance within the process would make
- assumptions about the construct which would fail.
-
-
- .. change::
- :tags: bug, postgresql, reflection
- :tickets: 6161
- :versions: 1.4.4
-
- Fixed issue in PostgreSQL reflection where a column expressing "NOT NULL"
- will supersede the nullability of a corresponding domain.
-
- .. change::
- :tags: bug, engine
- :tickets: 5929
- :versions: 1.4.0
-
- Fixed bug where the "schema_translate_map" feature failed to be taken into
- account for the use case of direct execution of
- :class:`_schema.DefaultGenerator` objects such as sequences, which included
- the case where they were "pre-executed" in order to generate primary key
- values when implicit_returning was disabled.
-
- .. change::
- :tags: bug, orm
- :tickets: 6001
- :versions: 1.4.0
-
- Fixed issue where the :class:`_mutable.MutableComposite` construct could be
- placed into an invalid state when the parent object was already loaded, and
- then covered by a subsequent query, due to the composite properties'
- refresh handler replacing the object with a new one not handled by the
- mutable extension.
-
-
- .. change::
- :tags: bug, types, postgresql
- :tickets: 6023
- :versions: 1.4.3
-
- Adjusted the psycopg2 dialect to emit an explicit PostgreSQL-style cast for
- bound parameters that contain ARRAY elements. This allows the full range of
- datatypes to function correctly within arrays. The asyncpg dialect already
- generated these internal casts in the final statement. This also includes
- support for array slice updates as well as the PostgreSQL-specific
- :meth:`_postgresql.ARRAY.contains` method.
-
- .. change::
- :tags: bug, mssql, reflection
- :tickets: 5921
-
- Fixed issue regarding SQL Server reflection for older SQL Server 2005
- version, a call to sp_columns would not proceed correctly without being
- prefixed with the EXEC keyword. This method is not used in current 1.4
- series.
-
-
-.. changelog::
- :version: 1.3.23
- :released: February 1, 2021
-
- .. change::
- :tags: bug, ext
- :tickets: 5836
-
- Fixed issue where the stringification that is sometimes called when
- attempting to generate the "key" for the ``.c`` collection on a selectable
- would fail if the column were an unlabeled custom SQL construct using the
- ``sqlalchemy.ext.compiler`` extension, and did not provide a default
- compilation form; while this seems like an unusual case, it can get invoked
- for some ORM scenarios such as when the expression is used in an "order by"
- in combination with joined eager loading. The issue is that the lack of a
- default compiler function was raising :class:`.CompileError` and not
- :class:`.UnsupportedCompilationError`.
-
- .. change::
- :tags: bug, postgresql
- :tickets: 5645
-
- For SQLAlchemy 1.3 only, setup.py pins pg8000 to a version lower than
- 1.16.6. Version 1.16.6 and above is supported by SQLAlchemy 1.4. Pull
- request courtesy Giuseppe Lumia.
-
- .. change::
- :tags: bug, postgresql
- :tickets: 5850
-
- Fixed issue where using :meth:`_schema.Table.to_metadata` (called
- :meth:`_schema.Table.tometadata` in 1.3) in conjunction with a PostgreSQL
- :class:`_postgresql.ExcludeConstraint` that made use of ad-hoc column
- expressions would fail to copy correctly.
-
- .. change::
- :tags: bug, sql
- :tickets: 5816
-
- Fixed bug where making use of the :meth:`.TypeEngine.with_variant` method
- on a :class:`.TypeDecorator` type would fail to take into account the
- dialect-specific mappings in use, due to a rule in :class:`.TypeDecorator`
- that was instead attempting to check for chains of :class:`.TypeDecorator`
- instances.
-
-
- .. change::
- :tags: bug, mysql, reflection
- :tickets: 5860
-
- Fixed bug where MySQL server default reflection would fail for numeric
- values with a negation symbol present.
-
-
- .. change::
- :tags: bug, oracle
- :tickets: 5813
-
- Fixed regression in Oracle dialect introduced by :ticket:`4894` in
- SQLAlchemy 1.3.11 where use of a SQL expression in RETURNING for an UPDATE
- would fail to compile, due to a check for "server_default" when an
- arbitrary SQL expression is not a column.
-
-
- .. change::
- :tags: usecase, mysql
- :tickets: 5808
-
- Casting to ``FLOAT`` is now supported in MySQL >= (8, 0, 17) and
- MariaDb >= (10, 4, 5).
-
- .. change::
- :tags: bug, mysql
- :tickets: 5898
-
- Fixed long-lived bug in MySQL dialect where the maximum identifier length
- of 255 was too long for names of all types of constraints, not just
- indexes, all of which have a size limit of 64. As metadata naming
- conventions can create too-long names in this area, apply the limit to the
- identifier generator within the DDL compiler.
-
- .. change::
- :tags: bug, oracle
- :tickets: 5812
-
- Fixed bug in Oracle dialect where retrieving a CLOB/BLOB column via
- :meth:`_dml.Insert.returning` would fail as the LOB value would need to be
- read when returned; additionally, repaired support for retrieval of Unicode
- values via RETURNING under Python 2.
-
- .. change::
- :tags: bug, mysql
- :tickets: 5821
-
- Fixed deprecation warnings that arose as a result of the release of PyMySQL
- 1.0, including deprecation warnings for the "db" and "passwd" parameters
- now replaced with "database" and "password".
-
-
- .. change::
- :tags: bug, mysql
- :tickets: 5800
-
- Fixed regression from SQLAlchemy 1.3.20 caused by the fix for
- :ticket:`5462` which adds double-parenthesis for MySQL functional
- expressions in indexes, as is required by the backend, this inadvertently
- extended to include arbitrary :func:`_sql.text` expressions as well as
- Alembic's internal textual component, which are required by Alembic for
- arbitrary index expressions which don't imply double parenthesis. The
- check has been narrowed to include only binary/ unary/functional
- expressions directly.
-
-.. changelog::
- :version: 1.3.22
- :released: December 18, 2020
-
- .. change::
- :tags: bug, oracle
- :tickets: 5784
- :versions: 1.4.0b2
-
- Fixed regression which occurred due to :ticket:`5755` which implemented
- isolation level support for Oracle. It has been reported that many Oracle
- accounts don't actually have permission to query the ``v$transaction``
- view so this feature has been altered to gracefully fallback when it fails
- upon database connect, where the dialect will assume "READ COMMITTED" is
- the default isolation level as was the case prior to SQLAlchemy 1.3.21.
- However, explicit use of the :meth:`_engine.Connection.get_isolation_level`
- method must now necessarily raise an exception, as Oracle databases with
- this restriction explicitly disallow the user from reading the current
- isolation level.
-
-.. changelog::
- :version: 1.3.21
- :released: December 17, 2020
-
- .. change::
- :tags: bug, orm
- :tickets: 5774
- :versions: 1.4.0b2
-
- Added a comprehensive check and an informative error message for the case
- where a mapped class, or a string mapped class name, is passed to
- :paramref:`_orm.relationship.secondary`. This is an extremely common error
- which warrants a clear message.
-
- Additionally, added a new rule to the class registry resolution such that
- with regards to the :paramref:`_orm.relationship.secondary` parameter, if a
- mapped class and its table are of the identical string name, the
- :class:`.Table` will be favored when resolving this parameter. In all
- other cases, the class continues to be favored if a class and table
- share the identical name.
-
- .. change::
- :tags: sqlite, usecase
- :tickets: 5685
-
- Added ``sqlite_with_rowid=False`` dialect keyword to enable creating
- tables as ``CREATE TABLE … WITHOUT ROWID``. Patch courtesy Sean Anderson.
-
- .. change::
- :tags: bug, sql
- :tickets: 5691
-
- A warning is emitted if a returning() method such as
- :meth:`_sql.Insert.returning` is called multiple times, as this does not
- yet support additive operation. Version 1.4 will support additive
- operation for this. Additionally, any combination of the
- :meth:`_sql.Insert.returning` and :meth:`_sql.ValuesBase.return_defaults`
- methods now raises an error as these methods are mutually exclusive;
- previously the operation would fail silently.
-
-
- .. change::
- :tags: bug, mssql
- :tickets: 5751
-
- Fixed bug where a CREATE INDEX statement was rendered incorrectly when
- both ``mssql-include`` and ``mssql_where`` were specified. Pull request
- courtesy @Adiorz.
-
- .. change::
- :tags: bug, postgresql, mysql
- :tickets: 5729
- :versions: 1.4.0b2
-
- Fixed regression introduced in 1.3.2 for the PostgreSQL dialect, also
- copied out to the MySQL dialect's feature in 1.3.18, where usage of a non
- :class:`_schema.Table` construct such as :func:`_sql.text` as the argument
- to :paramref:`_sql.Select.with_for_update.of` would fail to be accommodated
- correctly within the PostgreSQL or MySQL compilers.
-
-
- .. change::
- :tags: bug, mssql
- :tickets: 5646
-
- Added SQL Server code "01000" to the list of disconnect codes.
-
-
- .. change::
- :tags: usecase, postgresql
- :tickets: 5604
- :versions: 1.4.0b2
-
- Added new parameter :paramref:`_postgresql.ExcludeConstraint.ops` to the
- :class:`_postgresql.ExcludeConstraint` object, to support operator class
- specification with this constraint. Pull request courtesy Alon Menczer.
-
- .. change::
- :tags: bug, mysql, reflection
- :tickets: 5744
- :versions: 1.4.0b2
-
- Fixed issue where reflecting a server default on MariaDB only that
- contained a decimal point in the value would fail to be reflected
- correctly, leading towards a reflected table that lacked any server
- default.
-
-
- .. change::
- :tags: bug, orm
- :tickets: 5664
-
- Fixed bug in :meth:`_query.Query.update` where objects in the
- :class:`_ormsession.Session` that were already expired would be
- unnecessarily SELECTed individually when they were refreshed by the
- "evaluate"synchronize strategy.
-
- .. change::
- :tags: usecase, oracle
- :tickets: 5755
-
- Implemented support for the SERIALIZABLE isolation level for Oracle
- databases, as well as a real implementation for
- :meth:`_engine.Connection.get_isolation_level`.
-
- .. seealso::
-
- :ref:`oracle_isolation_level`
-
- .. change::
- :tags: mysql, sql
- :tickets: 5696
-
- Added missing keywords to the ``RESERVED_WORDS`` list for the MySQL
- dialect: ``action``, ``level``, ``mode``, ``status``, ``text``, ``time``.
- Pull request courtesy Oscar Batori.
-
- .. change::
- :tags: bug, orm
- :tickets: 5737
- :versions: 1.4.0b2
-
- Fixed bug involving the ``restore_load_context`` option of ORM events such
- as :meth:`_ormevent.InstanceEvents.load` such that the flag would not be
- carried along to subclasses which were mapped after the event handler were
- first established.
-
-
-
- .. change::
- :tags: bug, sql
- :tickets: 5656
-
- Fixed structural compiler issue where some constructs such as MySQL /
- PostgreSQL "on conflict / on duplicate key" would rely upon the state of
- the :class:`_sql.Compiler` object being fixed against their statement as
- the top level statement, which would fail in cases where those statements
- are branched from a different context, such as a DDL construct linked to a
- SQL statement.
-
-
- .. change::
- :tags: mssql, sqlite, reflection
- :tickets: 5661
-
- Fixed issue with composite primary key columns not being reported
- in the correct order. Patch courtesy @fulpm.
-
-.. changelog::
- :version: 1.3.20
- :released: October 12, 2020
-
- .. change::
- :tags: bug, orm
- :tickets: 4428
-
- An :class:`.ArgumentError` with more detail is now raised if the target
- parameter for :meth:`_query.Query.join` is set to an unmapped object.
- Prior to this change a less detailed ``AttributeError`` was raised.
- Pull request courtesy Ramon Williams.
-
- .. change::
- :tags: bug, mysql
- :tickets: 5568
-
- The "skip_locked" keyword used with ``with_for_update()`` will emit a
- warning when used on MariaDB backends, and will then be ignored. This is
- a deprecated behavior that will raise in SQLAlchemy 1.4, as an application
- that requests "skip locked" is looking for a non-blocking operation which
- is not available on those backends.
-
-
-
- .. change::
- :tags: bug, engine
- :tickets: 5599
-
- Fixed issue where a non-string object sent to
- :class:`_exc.SQLAlchemyError` or a subclass, as occurs with some third
- party dialects, would fail to stringify correctly. Pull request
- courtesy Andrzej Bartosiński.
-
- .. change::
- :tags: bug, sql
- :tickets: 5644
-
- Fixed issue where the ``pickle.dumps()`` operation against
- :class:`_expression.Over` construct would produce a recursion overflow.
-
- .. change::
- :tags: postgresql, usecase
- :tickets: 4392
-
- The psycopg2 dialect now support PostgreSQL multiple host connections, by
- passing host/port combinations to the query string. Pull request courtesy
- Ramon Williams.
-
- .. seealso::
-
- :ref:`psycopg2_multi_host`
-
- .. change::
- :tags: bug, mysql
- :tickets: 5617
-
- Fixed bug where an UPDATE statement against a JOIN using MySQL multi-table
- format would fail to include the table prefix for the target table if the
- statement had no WHERE clause, as only the WHERE clause were scanned to
- detect a "multi table update" at that particular point. The target
- is now also scanned if it's a JOIN to get the leftmost table as the
- primary table and the additional entries as additional FROM entries.
-
-
- .. change::
- :tags: bug, postgresql
- :tickets: 5518
-
- Adjusted the :meth:`_types.ARRAY.Comparator.any` and
- :meth:`_types.ARRAY.Comparator.all` methods to implement a straight "NOT"
- operation for negation, rather than negating the comparison operator.
-
- .. change::
- :tags: bug, pool
- :tickets: 5582
-
- Fixed issue where the following pool parameters were not being propagated
- to the new pool created when :meth:`_engine.Engine.dispose` were called:
- ``pre_ping``, ``use_lifo``. Additionally the ``recycle`` and
- ``reset_on_return`` parameter is now propagated for the
- :class:`_engine.AssertionPool` class.
-
- .. change::
- :tags: bug, ext, associationproxy
- :tickets: 5541, 5542
-
- An informative error is now raised when attempting to use an association
- proxy element as a plain column expression to be SELECTed from or used in a
- SQL function; this use case is not currently supported.
-
-
- .. change::
- :tags: bug, sql
- :tickets: 5618
-
- Fixed bug where an error was not raised in the case where a
- :func:`_sql.column` were added to more than one :func:`_sql.table` at a
- time. This raised correctly for the :class:`_schema.Column` and
- :class:`_schema.Table` objects. An :class:`_exc.ArgumentError` is now
- raised when this occurs.
-
- .. change::
- :tags: bug, orm
- :tickets: 4589
-
- Fixed issue where using a loader option against a string attribute name
- that is not actually a mapped attribute, such as a plain Python descriptor,
- would raise an uninformative AttributeError; a descriptive error is now
- raised.
-
-
-
- .. change::
- :tags: mysql, usecase
- :tickets: 5462
-
- Adjusted the MySQL dialect to correctly parenthesize functional index
- expressions as accepted by MySQL 8. Pull request courtesy Ramon Williams.
-
- .. change::
- :tags: bug, engine
- :tickets: 5632
-
- Repaired a function-level import that was not using SQLAlchemy's standard
- late-import system within the sqlalchemy.exc module.
-
-
- .. change::
- :tags: change, mysql
- :tickets: 5539
-
- Add new MySQL reserved words: ``cube``, ``lateral`` added in MySQL 8.0.1
- and 8.0.14, respectively; this indicates that these terms will be quoted if
- used as table or column identifier names.
-
- .. change::
- :tags: bug, mssql
- :tickets: 5592
-
- Fixed issue where a SQLAlchemy connection URI for Azure DW with
- ``authentication=ActiveDirectoryIntegrated`` (and no username+password)
- was not constructing the ODBC connection string in a way that was
- acceptable to the Azure DW instance.
-
- .. change::
- :tags: bug, postgresql
- :tickets: 5520
-
- Fixed issue where the :class:`_postgresql.ENUM` type would not consult the
- schema translate map when emitting a CREATE TYPE or DROP TYPE during the
- test to see if the type exists or not. Additionally, repaired an issue
- where if the same enum were encountered multiple times in a single DDL
- sequence, the "check" query would run repeatedly rather than relying upon a
- cached value.
-
-
- .. change::
- :tags: bug, tests
- :tickets: 5635
-
- Fixed incompatibilities in the test suite when running against Pytest 6.x.
-
-
-.. changelog::
- :version: 1.3.19
- :released: August 17, 2020
-
- .. change::
- :tags: usecase, py3k
- :tickets: #5357
-
- Added a ``**kw`` argument to the :meth:`.DeclarativeMeta.__init__` method.
- This allows a class to support the :pep:`487` metaclass hook
- ``__init_subclass__``. Pull request courtesy Ewen Gillies.
-
-
- .. change::
- :tags: bug, sql
- :tickets: 5470
-
- Repaired an issue where the "ORDER BY" clause rendering a label name rather
- than a complete expression, which is particularly important for SQL Server,
- would fail to occur if the expression were enclosed in a parenthesized
- grouping in some cases. This case has been added to test support. The
- change additionally adjusts the "automatically add ORDER BY columns when
- DISTINCT is present" behavior of ORM query, deprecated in 1.4, to more
- accurately detect column expressions that are already present.
-
- .. change::
- :tags: usecase, mysql
- :tickets: 5481
-
- The MySQL dialect will render FROM DUAL for a SELECT statement that has no
- FROM clause but has a WHERE clause. This allows things like "SELECT 1 WHERE
- EXISTS (subquery)" kinds of queries to be used as well as other use cases.
-
-
- .. change::
- :tags: bug, mssql, sql
- :tickets: 5467
-
- Fixed bug where the mssql dialect incorrectly escaped object names that
- contained ']' character(s).
-
- .. change::
- :tags: bug, reflection, sqlite, mssql
- :tickets: 5456
-
- Applied a sweep through all included dialects to ensure names that contain
- single or double quotes are properly escaped when querying system tables,
- for all :class:`.Inspector` methods that accept object names as an argument
- (e.g. table names, view names, etc). SQLite and MSSQL contained two
- quoting issues that were repaired.
-
- .. change::
- :tags: bug, mysql
- :tickets: 5411
-
- Fixed an issue where CREATE TABLE statements were not specifying the
- COLLATE keyword correctly.
-
- .. change::
- :tags: bug, datatypes, sql
- :tickets: 4733
-
- The ``LookupError`` message will now provide the user with up to four
- possible values that a column is constrained to via the :class:`.Enum`.
- Values longer than 11 characters will be truncated and replaced with
- ellipses. Pull request courtesy Ramon Williams.
-
- .. change::
- :tags: bug, postgresql
- :tickets: 5476
-
- Fixed issue where the return type for the various RANGE comparison
- operators would itself be the same RANGE type rather than BOOLEAN, which
- would cause an undesirable result in the case that a
- :class:`.TypeDecorator` that defined result-processing behavior were in
- use. Pull request courtesy Jim Bosch.
-
-
-
- .. change::
- :tags: bug, mysql
- :tickets: 5493
-
- Added MariaDB code 1927 to the list of "disconnect" codes, as recent
- MariaDB versions apparently use this code when the database server was
- stopped.
-
- .. change::
- :tags: usecase, declarative, orm
- :tickets: 5513
-
- The name of the virtual column used when using the
- :class:`_declarative.AbstractConcreteBase` and
- :class:`_declarative.ConcreteBase` classes can now be customized, to allow
- for models that have a column that is actually named ``type``. Pull
- request courtesy Jesse-Bakker.
-
- .. change::
- :tags: usecase, orm
- :tickets: 5494
-
- Adjusted the workings of the :meth:`_orm.Mapper.all_orm_descriptors`
- accessor to represent the attributes in the order that they are located in
- a deterministic way, assuming the use of Python 3.6 or higher which
- maintains the sorting order of class attributes based on how they were
- declared. This sorting is not guaranteed to match the declared order of
- attributes in all cases however; see the method documentation for the exact
- scheme.
-
-
-
- .. change::
- :tags: bug, sql
- :tickets: 5500
-
- Fixed issue where the
- :paramref:`_engine.Connection.execution_options.schema_translate_map`
- feature would not take effect when the :meth:`_schema.Sequence.next_value`
- function function for a :class:`_schema.Sequence` were used in the
- :paramref:`_schema.Column.server_default` parameter and the create table
- DDL were emitted.
-
-.. changelog::
- :version: 1.3.18
- :released: June 25, 2020
-
- .. change::
- :tags: bug, sqlite
- :tickets: 5395
-
- Added "exists" to the list of reserved words for SQLite so that this word
- will be quoted when used as a label or column name. Pull request courtesy
- Thodoris Sotiropoulos.
-
- .. change::
- :tags: bug, mssql
- :tickets: 5366, 5364
-
- Refined the logic used by the SQL Server dialect to interpret multi-part
- schema names that contain many dots, to not actually lose any dots if the
- name does not have bracking or quoting used, and additionally to support a
- "dbname" token that has many parts including that it may have multiple,
- independently-bracketed sections.
-
-
-
- .. change::
- :tags: bug, mssql, pyodbc
- :tickets: 5346
-
- Fixed an issue in the pyodbc connector such that a warning about pyodbc
- "drivername" would be emitted when using a totally empty URL. Empty URLs
- are normal when producing a non-connected dialect object or when using the
- "creator" argument to create_engine(). The warning now only emits if the
- driver name is missing but other parameters are still present.
-
- .. change::
- :tags: bug, mssql
- :tickets: 5373
-
- Fixed issue with assembling the ODBC connection string for the pyodbc
- DBAPI. Tokens containing semicolons and/or braces "{}" were not being
- correctly escaped, causing the ODBC driver to misinterpret the
- connection string attributes.
-
- .. change::
- :tags: usecase, orm
- :tickets: 5326
-
- Improve error message when using :meth:`_query.Query.filter_by` in
- a query where the first entity is not a mapped class.
-
- .. change::
- :tags: sql, schema
- :tickets: 5324
-
- Introduce :class:`.IdentityOptions` to store common parameters for
- sequences and identity columns.
-
- .. change::
- :tags: usecase, sql
- :tickets: 5309
-
- Added a ".schema" parameter to the :func:`_expression.table` construct,
- allowing ad-hoc table expressions to also include a schema name.
- Pull request courtesy Dylan Modesitt.
-
- .. change::
- :tags: bug, mssql
- :tickets: 5339
-
- Fixed issue where ``datetime.time`` parameters were being converted to
- ``datetime.datetime``, making them incompatible with comparisons like
- ``>=`` against an actual :class:`_mssql.TIME` column.
-
- .. change::
- :tags: bug, mssql
- :tickets: 5359
-
- Fixed an issue where the ``is_disconnect`` function in the SQL Server
- pyodbc dialect was incorrectly reporting the disconnect state when the
- exception message had a substring that matched a SQL Server ODBC error
- code.
-
- .. change::
- :tags: bug, engine
- :tickets: 5326
-
- Further refinements to the fixes to the "reset" agent fixed in
- :ticket:`5326`, which now emits a warning when it is not being correctly
- invoked and corrects for the behavior. Additional scenarios have been
- identified and fixed where this warning was being emitted.
-
-
- .. change::
- :tags: usecase, sqlite
- :tickets: 5297
-
- SQLite 3.31 added support for computed column. This change
- enables their support in SQLAlchemy when targeting SQLite.
-
- .. change::
- :tags: bug, schema
- :tickets: 5276
-
- Fixed issue where ``dialect_options`` were omitted when a
- database object (e.g., :class:`.Table`) was copied using
- :func:`.tometadata`.
-
- .. change::
- :tags: bug, sql
- :tickets: 5344
-
- Correctly apply self_group in type_coerce element.
-
- The type coerce element did not correctly apply grouping rules when using
- in an expression
-
- .. change::
- :tags: bug, oracle, reflection
- :tickets: 5421
-
- Fixed bug in Oracle dialect where indexes that contain the full set of
- primary key columns would be mistaken as the primary key index itself,
- which is omitted, even if there were multiples. The check has been refined
- to compare the name of the primary key constraint against the index name
- itself, rather than trying to guess based on the columns present in the
- index.
-
- .. change::
- :tags: change, sql, sybase
- :tickets: 5294
-
- Added ``.offset`` support to sybase dialect.
- Pull request courtesy Alan D. Snow.
-
- .. change::
- :tags: bug, engine
- :tickets: 5341
-
- Fixed issue in :class:`.URL` object where stringifying the object
- would not URL encode special characters, preventing the URL from being
- re-consumable as a real URL. Pull request courtesy Miguel Grinberg.
-
- .. change::
- :tags: usecase, mysql
- :tickets: 4860
-
- Implemented row-level locking support for mysql. Pull request courtesy
- Quentin Somerville.
-
- .. change::
- :tags: change, mssql
- :tickets: 5321
-
- Moved the ``supports_sane_rowcount_returning = False`` requirement from
- the ``PyODBCConnector`` level to the ``MSDialect_pyodbc`` since pyodbc
- does work properly in some circumstances.
-
- .. change::
- :tags: change, examples
-
- Added new option ``--raw`` to the examples.performance suite
- which will dump the raw profile test for consumption by any
- number of profiling visualizer tools. Removed the "runsnake"
- option as runsnake is very hard to build at this point;
-
- .. change::
- :tags: bug, sql
- :tickets: 5353
-
- Added :meth:`.Select.with_hint` output to the generic SQL string that is
- produced when calling ``str()`` on a statement. Previously, this clause
- would be omitted under the assumption that it was dialect specific.
- The hint text is presented within brackets to indicate the rendering
- of such hints varies among backends.
-
-
- .. change::
- :tags: usecase, orm
- :tickets: 5198
-
- Added a new parameter :paramref:`_orm.query_expression.default_expr` to the
- :func:`_orm.query_expression` construct, which will be appled to queries
- automatically if the :func:`_orm.with_expression` option is not used. Pull
- request courtesy Haoyu Sun.
-
-.. changelog::
- :version: 1.3.17
- :released: May 13, 2020
-
- .. change::
- :tags: bug, oracle
- :tickets: 5246
-
- Some modifications to how the cx_oracle dialect sets up per-column
- outputtype handlers for LOB and numeric datatypes to adjust for potential
- changes coming in cx_Oracle 8.
-
-
- .. change::
- :tags: bug, orm
- :tickets: 5288
-
- Fixed bug where using :func:`.with_polymorphic` as the target of a join via
- :meth:`.RelationshipComparator.of_type` on a mapper that already has a
- subquery-based with_polymorphic setting that's equivalent to the one
- requested would not correctly alias the ON clause in the join.
-
- .. change::
- :tags: bug, oracle, performance
- :tickets: 5314
-
- Changed the implementation of fetching CLOB and BLOB objects to use
- cx_Oracle's native implementation which fetches CLOB/BLOB objects inline
- with other result columns, rather than performing a separate fetch. As
- always, this can be disabled by setting auto_convert_lobs to False.
-
- As part of this change, the behavior of a CLOB that was given a blank
- string on INSERT now returns None on SELECT, which is now consistent with
- that of VARCHAR on Oracle.
-
-
- .. change::
- :tags: usecase, postgresql
- :tickets: 5265
-
- Added support for columns or type :class:`_sqltypes.ARRAY` of :class:`.Enum`,
- :class:`_postgresql.JSON` or :class:`_postgresql.JSONB` in PostgreSQL.
- Previously a workaround was required in these use cases.
-
-
- .. change::
- :tags: schema
- :tickets: 4138
-
- Add ``comment`` attribute to :class:`_schema.Column` ``__repr__`` method.
-
- .. change::
- :tags: bug, orm
- :tickets: 5303
-
- Fixed issue in the area of where loader options such as selectinload()
- interact with the baked query system, such that the caching of a query is
- not supposed to occur if the loader options themselves have elements such
- as with_polymorphic() objects in them that currently are not
- cache-compatible. The baked loader could sometimes not fully invalidate
- itself in these some of these scenarios leading to missed eager loads.
-
-
- .. change::
- :tags: bug, engine
- :tickets: 5326
-
- Fixed fairly critical issue where the DBAPI connection could be returned to
- the connection pool while still in an un-rolled-back state. The reset agent
- responsible for rolling back the connection could be corrupted in the case
- that the transaction was "closed" without being rolled back or committed,
- which can occur in some scenarios when using ORM sessions and emitting
- .close() in a certain pattern involving savepoints. The fix ensures that
- the reset agent is always active.
-
-
- .. change::
- :tags: bug, orm
- :tickets: 5304
-
- Modified the internal "identity set" implementation, which is a set that
- hashes objects on their id() rather than their hash values, to not actually
- call the ``__hash__()`` method of the objects, which are typically
- user-mapped objects. Some methods were calling this method as a side
- effect of the implementation.
-
-
- .. change::
- :tags: usecase, postgresql
- :tickets: 5266
-
- Raise an explicit :class:`.exc.CompileError` when adding a table with a
- column of type :class:`_sqltypes.ARRAY` of :class:`.Enum` configured with
- :paramref:`.Enum.native_enum` set to ``False`` when
- :paramref:`.Enum.create_constraint` is not set to ``False``
-
- .. change::
- :tags: bug, schema
- :tickets: 5298
-
- Fixed issue where an :class:`.Index` that is deferred in being associated
- with a table, such as as when it contains a :class:`.Column` that is not
- associated with any :class:`.Table` yet, would fail to attach correctly if
- it also contained a non table-oriented expression.
-
-
- .. change::
- :tags: change, firebird
- :tickets: 5278
-
- Adjusted dialect loading for ``firebird://`` URIs so the external
- sqlalchemy-firebird dialect will be used if it has been installed,
- otherwise fall back to the (now deprecated) internal Firebird dialect.
-
- .. change::
- :tags: bug, mssql, reflection
- :tickets: 5255
-
- Fix a regression introduced by the reflection of computed column in
- MSSQL when using the legacy TDS version 4.2. The dialect will try
- to detect the protocol version of first connect and run in compatibility
- mode if it cannot detect it.
-
- .. change::
- :tags: bug, mssql, reflection
- :tickets: 5271
-
- Fix a regression introduced by the reflection of computed column in
- MSSQL when using SQL server versions before 2012, which does not support
- the ``concat`` function.
-
- .. change::
- :tags: bug, orm
- :tickets: 5269
-
- An informative error message is raised when an ORM many-to-one comparison
- is attempted against an object that is not an actual mapped instance.
- Comparisons such as those to scalar subqueries aren't supported;
- generalized comparison with subqueries is better achieved using
- :meth:`~.RelationshipProperty.Comparator.has`.
-
-
- .. change::
- :tags: usecase, orm
- :tickets: 5262
-
- Added an accessor :attr:`.ColumnProperty.Comparator.expressions` which
- provides access to the group of columns mapped under a multi-column
- :class:`.ColumnProperty` attribute.
-
-
- .. change::
- :tags: bug, schema
- :tickets: 5316
-
- A warning is emitted when making use of the :attr:`.MetaData.sorted_tables`
- attribute as well as the :func:`_schema.sort_tables` function, and the
- given tables cannot be correctly sorted due to a cyclic dependency between
- foreign key constraints. In this case, the functions will no longer sort
- the involved tables by foreign key, and a warning will be emitted. Other
- tables that are not part of the cycle will still be returned in dependency
- order. Previously, the sorted_table routines would return a collection that
- would unconditionally omit all foreign keys when a cycle was detected, and
- no warning was emitted.
-
-
- .. change::
- :tags: orm, usecase
- :tickets: 5237
-
- Introduce :paramref:`_orm.relationship.sync_backref` flag in a relationship
- to control if the synchronization events that mutate the in-Python
- attributes are added. This supersedes the previous change :ticket:`5149`,
- which warned that ``viewonly=True`` relationship target of a
- back_populates or backref configuration would be disallowed.
-
-.. changelog::
- :version: 1.3.16
- :released: April 7, 2020
-
- .. change::
- :tags: oracle, usecase
- :tickets: 5200
-
- Implemented AUTOCOMMIT isolation level for Oracle when using cx_Oracle.
- Also added a fixed default isolation level of READ COMMITTED for Oracle.
-
-
- .. change::
- :tags: bug, mysql
- :tickets: 5239
-
- Fixed issue in MySQL dialect when connecting to a pseudo-MySQL database
- such as that provided by ProxySQL, the up front check for isolation level
- when it returns no row will not prevent the dialect from continuing to
- connect. A warning is emitted that the isolation level could not be
- detected.
-
-
- .. change::
- :tags: bug, tests
- :tickets: 5201
-
- Fixed an issue that prevented the test suite from running with the
- recently released py.test 5.4.0.
-
-
- .. change::
- :tags: bug, oracle, reflection
- :tickets: 5146
-
- Fixed regression / incorrect fix caused by fix for :ticket:`5146` where the
- Oracle dialect reads from the "all_tab_comments" view to get table comments
- but fails to accommodate for the current owner of the table being
- requested, causing it to read the wrong comment if multiple tables of the
- same name exist in multiple schemas.
-
-
- .. change::
- :tags: types, enum
- :tickets: 5183
-
- The :class:`.Enum` type now supports the parameter :paramref:`.Enum.length`
- to specify the length of the VARCHAR column to create when using
- non native enums by setting :paramref:`.Enum.native_enum` to ``False``
-
- .. change::
- :tags: bug, orm
- :tickets: 5228
-
- Fixed bug in :func:`_orm.selectinload` loading option where two or more
- loaders that represent different relationships with the same string key
- name as referenced from a single :func:`_orm.with_polymorphic` construct
- with multiple subclass mappers would fail to invoke each subqueryload
- separately, instead making use of a single string-based slot that would
- prevent the other loaders from being invoked.
-
-
- .. change::
- :tags: schema, reflection
- :tickets: 5063
-
- Added support for reflection of "computed" columns, which are now returned
- as part of the structure returned by :meth:`_reflection.Inspector.get_columns`.
- When reflecting full :class:`_schema.Table` objects, computed columns will
- be represented using the :class:`.Computed` construct.
-
- .. change::
- :tags: orm, performance
- :tickets: 5162
-
- Modified the queries used by subqueryload and selectinload to no longer
- ORDER BY the primary key of the parent entity; this ordering was there to
- allow the rows as they come in to be copied into lists directly with a
- minimal level of Python-side collation. However, these ORDER BY clauses
- can negatively impact the performance of the query as in many scenarios
- these columns are derived from a subquery or are otherwise not actual
- primary key columns such that SQL planners cannot make use of indexes. The
- Python-side collation uses the native itertools.group_by() to collate the
- incoming rows, and has been modified to allow multiple
- row-groups-per-parent to be assembled together using list.extend(), which
- should still allow for relatively fast Python-side performance. There will
- still be an ORDER BY present for a relationship that includes an explicit
- order_by parameter, however this is the only ORDER BY that will be added to
- the query for both kinds of loading.
-
- .. change::
- :tags: mssql, mysql, oracle, usecase
- :tickets: 5137
-
- Added support for :meth:`.ColumnOperators.is_distinct_from` and
- :meth:`.ColumnOperators.isnot_distinct_from` to SQL Server,
- MySQL, and Oracle.
-
- .. change::
- :tags: sqlite, usecase
- :tickets: 5164
-
- Implemented AUTOCOMMIT isolation level for SQLite when using pysqlite.
-
- .. change::
- :tags: bug, postgresql
- :tickets: 5205
-
- Fixed issue where a "covering" index, e.g. those which have an INCLUDE
- clause, would be reflected including all the columns in INCLUDE clause as
- regular columns. A warning is now emitted if these additional columns are
- detected indicating that they are currently ignored. Note that full
- support for "covering" indexes is part of :ticket:`4458`. Pull request
- courtesy Marat Sharafutdinov.
-
- .. change::
- :tags: sql, types
- :tickets: 5052
-
- Add ability to literal compile a :class:`DateTime`, :class:`Date`
- or :class:`Time` when using the string dialect for debugging purposes.
- This change does not impact real dialect implementation that retain
- their current behavior.
-
- .. change::
- :tags: installer
- :tickets: 5207
-
- Ensured that the "pyproject.toml" file is not included in builds, as the
- presence of this file indicates to pip that a pep-517 installation process
- should be used. As this mode of operation appears to be not well supported
- by current tools / distros, these problems are avoided within the scope
- of SQLAlchemy installation by omitting the file.
-
-
- .. change::
- :tags: bug, orm
- :tickets: 5210
-
- Fixed issue where a lazyload that uses session-local "get" against a target
- many-to-one relationship where an object with the correct primary key is
- present, however it's an instance of a sibling class, does not correctly
- return None as is the case when the lazy loader actually emits a load for
- that row.
-
- .. change::
- :tags: bug, orm, declarative
- :tickets: 5238
-
- The string argument accepted as the first positional argument by the
- :func:`_orm.relationship` function when using the Declarative API is no longer
- interpreted using the Python ``eval()`` function; instead, the name is dot
- separated and the names are looked up directly in the name resolution
- dictionary without treating the value as a Python expression. However,
- passing a string argument to the other :func:`_orm.relationship` parameters
- that necessarily must accept Python expressions will still use ``eval()``;
- the documentation has been clarified to ensure that there is no ambiguity
- that this is in use.
-
- .. seealso::
-
- :ref:`declarative_relationship_eval` - details on string evaluation
-
-.. changelog::
- :version: 1.3.15
- :released: March 11, 2020
-
- .. change::
- :tags: bug, orm
- :tickets: 5194
-
- Adjusted the error message emitted by :meth:`_query.Query.join` when a left hand
- side can't be located that the :meth:`_query.Query.select_from` method is the
- best way to resolve the issue. Also, within the 1.3 series, used a
- deterministic ordering when determining the FROM clause from a given column
- entity passed to :class:`_query.Query` so that the same expression is determined
- each time.
-
-
- .. change::
- :tags: orm, bug
- :tickets: 5196
-
- Fixed regression in 1.3.14 due to :ticket:`4849` where a sys.exc_info()
- call failed to be invoked correctly when a flush error would occur. Test
- coverage has been added for this exception case.
-
-
-.. changelog::
- :version: 1.3.14
- :released: March 10, 2020
-
- .. change::
- :tags: bug, sql, postgresql
- :tickets: 5181
-
- Fixed bug where a CTE of an INSERT/UPDATE/DELETE that also uses RETURNING
- could then not be SELECTed from directly, as the internal state of the
- compiler would try to treat the outer SELECT as a DELETE statement itself
- and access nonexistent state.
-
-
- .. change::
- :tags: bug, orm
- :tickets: 5110
-
- Fixed regression caused in 1.3.13 by :ticket:`5056` where a refactor of the
- ORM path registry system made it such that a path could no longer be
- compared to an empty tuple, which can occur in a particular kind of joined
- eager loading path. The "empty tuple" use case has been resolved so that
- the path registry is compared to a path registry in all cases; the
- :class:`.PathRegistry` object itself now implements ``__eq__()`` and
- ``__ne__()`` methods which will take place for all equality comparisons and
- continue to succeed in the not anticipated case that a non-
- :class:`.PathRegistry` object is compared, while emitting a warning that
- this object should not be the subject of the comparison.
-
-
-
- .. change::
- :tags: bug, orm
- :tickets: 5149
-
- Setting a relationship to viewonly=True which is also the target of a
- back_populates or backref configuration will now emit a warning and
- eventually be disallowed. back_populates refers specifically to mutation
- of an attribute or collection, which is disallowed when the attribute is
- subject to viewonly=True. The viewonly attribute is not subject to
- persistence behaviors which means it will not reflect correct results
- when it is locally mutated.
-
- .. change::
- :tags: bug, oracle
- :tickets: 5146
-
- Fixed a reflection bug where table comments could only be retrieved for
- tables actually owned by the user but not for tables visible to the user
- but owned by someone else. Pull request courtesy Dave Hirschfeld.
-
- .. change::
- :tags: bug, performance
- :tickets: 5180
-
- Revised an internal change to the test system added as a result of
- :ticket:`5085` where a testing-related module per dialect would be loaded
- unconditionally upon making use of that dialect, pulling in SQLAlchemy's
- testing framework as well as the ORM into the module import space. This
- would only impact initial startup time and memory to a modest extent,
- however it's best that these additional modules aren't reverse-dependent on
- straight Core usage.
-
- .. change::
- :tags: bug, installation
- :tickets: 5138
-
- Vendored the ``inspect.formatannotation`` function inside of
- ``sqlalchemy.util.compat``, which is needed for the vendored version of
- ``inspect.formatargspec``. The function is not documented in cPython and
- is not guaranteed to be available in future Python versions.
-
-
- .. change::
- :tags: bug, mssql
- :tickets: 5132
-
- Fixed issue where the :class:`_mssql.DATETIMEOFFSET` type would not
- accommodate for the ``None`` value, introduced as part of the series of
- fixes for this type first introduced in :ticket:`4983`, :ticket:`5045`.
- Additionally, added support for passing a backend-specific date formatted
- string through this type, as is typically allowed for date/time types on
- most other DBAPIs.
-
- .. change::
- :tags: bug, engine
- :tickets: 5182
-
- Expanded the scope of cursor/connection cleanup when a statement is
- executed to include when the result object fails to be constructed, or an
- after_cursor_execute() event raises an error, or autocommit / autoclose
- fails. This allows the DBAPI cursor to be cleaned up on failure and for
- connectionless execution allows the connection to be closed out and
- returned to the connection pool, where previously it waiting until garbage
- collection would trigger a pool return.
-
- .. change::
- :tags: bug, postgresql
- :tickets: 5158
-
- Fixed issue where the "schema_translate_map" feature would not work with a
- PostgreSQL native enumeration type (i.e. :class:`.Enum`,
- :class:`_postgresql.ENUM`) in that while the "CREATE TYPE" statement would
- be emitted with the correct schema, the schema would not be rendered in
- the CREATE TABLE statement at the point at which the enumeration was
- referenced.
-
-
- .. change::
- :tags: usecase, ext
- :tickets: 5114
-
- Added keyword arguments to the :meth:`.MutableList.sort` function so that a
- key function as well as the "reverse" keyword argument can be provided.
-
-
- .. change::
- :tags: bug, general, py3k
- :tickets: 4849
-
- Applied an explicit "cause" to most if not all internally raised exceptions
- that are raised from within an internal exception catch, to avoid
- misleading stacktraces that suggest an error within the handling of an
- exception. While it would be preferable to suppress the internally caught
- exception in the way that the ``__suppress_context__`` attribute would,
- there does not as yet seem to be a way to do this without suppressing an
- enclosing user constructed context, so for now it exposes the internally
- caught exception as the cause so that full information about the context
- of the error is maintained.
-
- .. change::
- :tags: orm, bug
- :tickets: 5121
-
- Fixed an additional regression in the same area as that of :ticket:`5080`
- introduced in 1.3.0b3 via :ticket:`4468` where the ability to create a
- joined option across a :func:`.with_polymorphic` into a relationship
- against the base class of that with_polymorphic, and then further into
- regular mapped relationships would fail as the base class component would
- not add itself to the load path in a way that could be located by the
- loader strategy. The changes applied in :ticket:`5080` have been further
- refined to also accommodate this scenario.
-
- .. change::
- :tags: bug, postgresql, reflection
- :tickets: 5170
-
- Fixed bug where PostgreSQL reflection of CHECK constraints would fail to
- parse the constraint if the SQL text contained newline characters. The
- regular expression has been adjusted to accommodate for this case. Pull
- request courtesy Eric Borczuk.
-
- .. change::
- :tags: usecase, orm
- :tickets: 5129
-
- Added a new flag :paramref:`.InstanceEvents.restore_load_context` and
- :paramref:`.SessionEvents.restore_load_context` which apply to the
- :meth:`.InstanceEvents.load`, :meth:`.InstanceEvents.refresh`, and
- :meth:`.SessionEvents.loaded_as_persistent` events, which when set will
- restore the "load context" of the object after the event hook has been
- called. This ensures that the object remains within the "loader context"
- of the load operation that is already ongoing, rather than the object being
- transferred to a new load context due to refresh operations which may have
- occurred in the event. A warning is now emitted when this condition occurs,
- which recommends use of the flag to resolve this case. The flag is
- "opt-in" so that there is no risk introduced to existing applications.
-
- The change additionally adds support for the ``raw=True`` flag to
- session lifecycle events.
-
- .. change::
- :tags: bug, mysql
- :tickets: 5173
-
- Fixed issue in MySQL :meth:`.mysql.Insert.on_duplicate_key_update` construct
- where using a SQL function or other composed expression for a column argument
- would not properly render the ``VALUES`` keyword surrounding the column
- itself.
-
-.. changelog::
- :version: 1.3.13
- :released: January 22, 2020
-
- .. change::
- :tags: bug, postgresql
- :tickets: 5039
-
- Fixed issue where the PostgreSQL dialect would fail to parse a reflected
- CHECK constraint that was a boolean-valued function (as opposed to a
- boolean-valued expression).
-
- .. change::
- :tags: bug, ext
- :tickets: 5086
-
- Fixed bug in sqlalchemy.ext.serializer where a unique
- :class:`.BindParameter` object could conflict with itself if it were
- present in the mapping itself, as well as the filter condition of the
- query, as one side would be used against the non-deserialized version and
- the other side would use the deserialized version. Logic is added to
- :class:`.BindParameter` similar to its "clone" method which will uniquify
- the parameter name upon deserialize so that it doesn't conflict with its
- original.
-
-
- .. change::
- :tags: usecase, sql
- :tickets: 5079
-
- A function created using :class:`.GenericFunction` can now specify that the
- name of the function should be rendered with or without quotes by assigning
- the :class:`.quoted_name` construct to the .name element of the object.
- Prior to 1.3.4, quoting was never applied to function names, and some
- quoting was introduced in :ticket:`4467` but no means to force quoting for
- a mixed case name was available. Additionally, the :class:`.quoted_name`
- construct when used as the name will properly register its lowercase name
- in the function registry so that the name continues to be available via the
- ``func.`` registry.
-
- .. seealso::
-
- :class:`.GenericFunction`
-
-
- .. change::
- :tags: bug, engine
- :tickets: 5048
-
- Fixed issue where the collection of value processors on a
- :class:`.Compiled` object would be mutated when "expanding IN" parameters
- were used with a datatype that has bind value processors; in particular,
- this would mean that when using statement caching and/or baked queries, the
- same compiled._bind_processors collection would be mutated concurrently.
- Since these processors are the same function for a given bind parameter
- namespace every time, there was no actual negative effect of this issue,
- however, the execution of a :class:`.Compiled` object should never be
- causing any changes in its state, especially given that they are intended
- to be thread-safe and reusable once fully constructed.
-
-
- .. change::
- :tags: tests, postgresql
- :tickets: 5057
-
- Improved detection of two phase transactions requirement for the PostgreSQL
- database by testing that max_prepared_transactions is set to a value
- greater than 0. Pull request courtesy Federico Caselli.
-
-
- .. change::
- :tags: bug, orm, engine
- :tickets: 5056, 5050, 5071
-
- Added test support and repaired a wide variety of unnecessary reference
- cycles created for short-lived objects, mostly in the area of ORM queries.
- Thanks much to Carson Ip for the help on this.
-
-
- .. change::
- :tags: orm, bug
- :tickets: 5107
-
- Fixed regression in loader options introduced in 1.3.0b3 via :ticket:`4468`
- where the ability to create a loader option using
- :meth:`.PropComparator.of_type` targeting an aliased entity that is an
- inheriting subclass of the entity which the preceding relationship refers
- to would fail to produce a matching path. See also :ticket:`5082` fixed
- in this same release which involves a similar kind of issue.
-
- .. change::
- :tags: bug, tests
- :tickets: 4946
-
- Fixed a few test failures which would occur on Windows due to SQLite file
- locking issues, as well as some timing issues in connection pool related
- tests; pull request courtesy Federico Caselli.
-
-
- .. change::
- :tags: orm, bug
- :tickets: 5082
-
- Fixed regression in joined eager loading introduced in 1.3.0b3 via
- :ticket:`4468` where the ability to create a joined option across a
- :func:`.with_polymorphic` into a polymorphic subclass using
- :meth:`.RelationshipProperty.of_type` and then further along regular mapped
- relationships would fail as the polymorphic subclass would not add itself
- to the load path in a way that could be located by the loader strategy. A
- tweak has been made to resolve this scenario.
-
-
- .. change::
- :tags: performance, orm
-
- Identified a performance issue in the system by which a join is constructed
- based on a mapped relationship. The clause adaption system would be used
- for the majority of join expressions including in the common case where no
- adaptation is needed. The conditions under which this adaptation occur
- have been refined so that average non-aliased joins along a simple
- relationship without a "secondary" table use about 70% less function calls.
-
-
- .. change::
- :tags: usecase, postgresql
- :tickets: 5040
-
- Added support for prefixes to the :class:`_expression.CTE` construct, to allow
- support for Postgresql 12 "MATERIALIZED" and "NOT MATERIALIZED" phrases.
- Pull request courtesy Marat Sharafutdinov.
-
- .. seealso::
-
- :meth:`_expression.HasCTE.cte`
-
- .. change::
- :tags: bug, mssql
- :tickets: 5045
-
- Fixed issue where a timezone-aware ``datetime`` value being converted to
- string for use as a parameter value of a :class:`_mssql.DATETIMEOFFSET`
- column was omitting the fractional seconds.
-
- .. change::
- :tags: bug, orm
- :tickets: 5068
-
- Repaired a warning in the ORM flush process that was not covered by test
- coverage when deleting objects that use the "version_id" feature. This
- warning is generally unreachable unless using a dialect that sets the
- "supports_sane_rowcount" flag to False, which is not typically the case
- however is possible for some MySQL configurations as well as older Firebird
- drivers, and likely some third party dialects.
-
- .. change::
- :tags: bug, orm
- :tickets: 5065
-
- Fixed bug where usage of joined eager loading would not properly wrap the
- query inside of a subquery when :meth:`_query.Query.group_by` were used against
- the query. When any kind of result-limiting approach is used, such as
- DISTINCT, LIMIT, OFFSET, joined eager loading embeds the row-limited query
- inside of a subquery so that the collection results are not impacted. For
- some reason, the presence of GROUP BY was never included in this criterion,
- even though it has a similar effect as using DISTINCT. Additionally, the
- bug would prevent using GROUP BY at all for a joined eager load query for
- most database platforms which forbid non-aggregated, non-grouped columns
- from being in the query, as the additional columns for the joined eager
- load would not be accepted by the database.
-
-
-
-.. changelog::
- :version: 1.3.12
- :released: December 16, 2019
-
- .. change::
- :tags: bug, sql
- :tickets: 5028
-
- Fixed bug where "distinct" keyword passed to :func:`_expression.select` would not
- treat a string value as a "label reference" in the same way that the
- :meth:`_expression.select.distinct` does; it would instead raise unconditionally. This
- keyword argument and the others passed to :func:`_expression.select` will ultimately
- be deprecated for SQLAlchemy 2.0.
-
-
- .. change::
- :tags: bug, orm
- :tickets: 4997
-
- Fixed issue involving ``lazy="raise"`` strategy where an ORM delete of an
- object would raise for a simple "use-get" style many-to-one relationship
- that had lazy="raise" configured. This is inconsistent vs. the change
- introduced in 1.3 as part of :ticket:`4353`, where it was established that
- a history operation that does not expect emit SQL should bypass the
- ``lazy="raise"`` check, and instead effectively treat it as
- ``lazy="raise_on_sql"`` for this case. The fix adjusts the lazy loader
- strategy to not raise for the case where the lazy load was instructed that
- it should not emit SQL if the object were not present.
-
- .. change::
- :tags: bug, sql
-
- Changed the text of the exception for "Can't resolve label reference" to
- include other kinds of label coercions, namely that "DISTINCT" is also in
- this category under the PostgreSQL dialect.
-
-
- .. change::
- :tags: bug, orm
- :tickets: 5000
-
- Fixed regression introduced in 1.3.0 related to the association proxy
- refactor in :ticket:`4351` that prevented :func:`.composite` attributes
- from working in terms of an association proxy that references them.
-
- .. change::
- :tags: bug, mssql
- :tickets: 4983
-
- Repaired support for the :class:`_mssql.DATETIMEOFFSET` datatype on PyODBC,
- by adding PyODBC-level result handlers as it does not include native
- support for this datatype. This includes usage of the Python 3 "timezone"
- tzinfo subclass in order to set up a timezone, which on Python 2 makes
- use of a minimal backport of "timezone" in sqlalchemy.util.
-
-
- .. change::
- :tags: bug, orm
- :tickets: 4993
-
- Setting persistence-related flags on :func:`_orm.relationship` while also
- setting viewonly=True will now emit a regular warning, as these flags do
- not make sense for a viewonly=True relationship. In particular, the
- "cascade" settings have their own warning that is generated based on the
- individual values, such as "delete, delete-orphan", that should not apply
- to a viewonly relationship. Note however that in the case of "cascade",
- these settings are still erroneously taking effect even though the
- relationship is set up as "viewonly". In 1.4, all persistence-related
- cascade settings will be disallowed on a viewonly=True relationship in
- order to resolve this issue.
-
- .. change::
- :tags: bug, sqlite
- :tickets: 5014
-
- Fixed issue to workaround SQLite's behavior of assigning "numeric" affinity
- to JSON datatypes, first described at :ref:`change_3850`, which returns
- scalar numeric JSON values as a number and not as a string that can be JSON
- deserialized. The SQLite-specific JSON deserializer now gracefully
- degrades for this case as an exception and bypasses deserialization for
- single numeric values, as from a JSON perspective they are already
- deserialized.
-
-
-
- .. change::
- :tags: bug, orm, py3k
- :tickets: 4990
-
- Fixed issue where when assigning a collection to itself as a slice, the
- mutation operation would fail as it would first erase the assigned
- collection inadvertently. As an assignment that does not change the
- contents should not generate events, the operation is now a no-op. Note
- that the fix only applies to Python 3; in Python 2, the ``__setitem__``
- hook isn't called in this case; ``__setslice__`` is used instead which
- recreates the list item-by-item in all cases.
-
- .. change::
- :tags: bug, orm
- :tickets: 5034
-
- Fixed issue where by if the "begin" of a transaction failed at the Core
- engine/connection level, such as due to network error or database is locked
- for some transactional recipes, within the context of the :class:`.Session`
- procuring that connection from the connection pool and then immediately
- returning it, the ORM :class:`.Session` would not close the connection
- despite this connection not being stored within the state of that
- :class:`.Session`. This would lead to the connection being cleaned out by
- the connection pool weakref handler within garbage collection which is an
- unpreferred codepath that in some special configurations can emit errors in
- standard error.
-
-.. changelog::
- :version: 1.3.11
- :released: November 11, 2019
-
- .. change::
- :tags: bug, mssql
- :tickets: 4973
-
- Fixed issue in MSSQL dialect where an expression-based OFFSET value in a
- SELECT would be rejected, even though the dialect can render this
- expression inside of a ROW NUMBER-oriented LIMIT/OFFSET construct.
-
-
- .. change::
- :tags: orm, usecase
- :tickets: 4934
-
- Added accessor :meth:`_query.Query.is_single_entity` to :class:`_query.Query`, which
- will indicate if the results returned by this :class:`_query.Query` will be a
- list of ORM entities, or a tuple of entities or column expressions.
- SQLAlchemy hopes to improve upon the behavior of single entity / tuples in
- future releases such that the behavior would be explicit up front, however
- this attribute should be helpful with the current behavior. Pull request
- courtesy Patrick Hayes.
-
- .. change::
- :tags: bug, mysql
- :tickets: 4945
-
- Added "Connection was killed" message interpreted from the base
- pymysql.Error class in order to detect closed connection, based on reports
- that this message is arriving via a pymysql.InternalError() object which
- indicates pymysql is not handling it correctly.
-
- .. change::
- :tags: bug, orm
- :tickets: 4954
-
- The :paramref:`_orm.relationship.omit_join` flag was not intended to be
- manually set to True, and will now emit a warning when this occurs. The
- omit_join optimization is detected automatically, and the ``omit_join``
- flag was only intended to disable the optimization in the hypothetical case
- that the optimization may have interfered with correct results, which has
- not been observed with the modern version of this feature. Setting the
- flag to True when it is not automatically detected may cause the selectin
- load feature to not work correctly when a non-default primary join
- condition is in use.
-
-
- .. change::
- :tags: bug, orm
- :tickets: 4915
-
- A warning is emitted if a primary key value is passed to :meth:`_query.Query.get`
- that consists of None for all primary key column positions. Previously,
- passing a single None outside of a tuple would raise a ``TypeError`` and
- passing a composite None (tuple of None values) would silently pass
- through. The fix now coerces the single None into a tuple where it is
- handled consistently with the other None conditions. Thanks to Lev
- Izraelit for the help with this.
-
-
- .. change::
- :tags: bug, orm
- :tickets: 4947
-
- The :class:`.BakedQuery` will not cache a query that was modified by a
- :meth:`.QueryEvents.before_compile` event, so that compilation hooks that
- may be applying ad-hoc modifications to queries will take effect on each
- run. In particular this is helpful for events that modify queries used in
- lazy loading as well as eager loading such as "select in" loading. In
- order to re-enable caching for a query modified by this event, a new
- flag ``bake_ok`` is added; see :ref:`baked_with_before_compile` for
- details.
-
- A longer term plan to provide a new form of SQL caching should solve this
- kind of issue more comprehensively.
-
- .. change::
- :tags: bug, tests
- :tickets: 4920
-
- Fixed test failures which would occur with newer SQLite as of version 3.30
- or greater, due to their addition of nulls ordering syntax as well as new
- restrictions on aggregate functions. Pull request courtesy Nils Philippsen.
-
-
-
- .. change::
- :tags: bug, installation, windows
- :tickets: 4967
-
- Added a workaround for a setuptools-related failure that has been observed
- as occurring on Windows installations, where setuptools is not correctly
- reporting a build error when the MSVC build dependencies are not installed
- and therefore not allowing graceful degradation into non C extensions
- builds.
-
- .. change::
- :tags: bug, sql, py3k
- :tickets: 4931
-
- Changed the ``repr()`` of the :class:`.quoted_name` construct to use
- regular string repr() under Python 3, rather than running it through
- "backslashreplace" escaping, which can be misleading.
-
- .. change::
- :tags: bug, oracle, firebird
- :tickets: 4931
-
- Modified the approach of "name normalization" for the Oracle and Firebird
- dialects, which converts from the UPPERCASE-as-case-insensitive convention
- of these dialects into lowercase-as-case-insensitive for SQLAlchemy, to not
- automatically apply the :class:`.quoted_name` construct to a name that
- matches itself under upper or lower case conversion, as is the case for
- many non-european characters. All names used within metadata structures
- are converted to :class:`.quoted_name` objects in any case; the change
- here would only affect the output of some inspection functions.
-
- .. change::
- :tags: bug, schema
- :tickets: 4911
-
- Fixed bug where a table that would have a column label overlap with a plain
- column name, such as "foo.id AS foo_id" vs. "foo.foo_id", would prematurely
- generate the ``._label`` attribute for a column before this overlap could
- be detected due to the use of the ``index=True`` or ``unique=True`` flag on
- the column in conjunction with the default naming convention of
- ``"column_0_label"``. This would then lead to failures when ``._label``
- were used later to generate a bound parameter name, in particular those
- used by the ORM when generating the WHERE clause for an UPDATE statement.
- The issue has been fixed by using an alternate ``._label`` accessor for DDL
- generation that does not affect the state of the :class:`_schema.Column`. The
- accessor also bypasses the key-deduplication step as it is not necessary
- for DDL, the naming is now consistently ``"<tablename>_<columnname>"``
- without any subsequent numeric symbols when used in DDL.
-
-
-
- .. change::
- :tags: bug, engine
- :tickets: 4902
-
- Fixed bug where parameter repr as used in logging and error reporting needs
- additional context in order to distinguish between a list of parameters for
- a single statement and a list of parameter lists, as the "list of lists"
- structure could also indicate a single parameter list where the first
- parameter itself is a list, such as for an array parameter. The
- engine/connection now passes in an additional boolean indicating how the
- parameters should be considered. The only SQLAlchemy backend that expects
- arrays as parameters is that of psycopg2 which uses pyformat parameters,
- so this issue has not been too apparent, however as other drivers that use
- positional gain more features it is important that this be supported. It
- also eliminates the need for the parameter repr function to guess based on
- the parameter structure passed.
-
- .. change::
- :tags: usecase, schema
- :tickets: 4894
-
- Added DDL support for "computed columns"; these are DDL column
- specifications for columns that have a server-computed value, either upon
- SELECT (known as "virtual") or at the point of which they are INSERTed or
- UPDATEd (known as "stored"). Support is established for Postgresql, MySQL,
- Oracle SQL Server and Firebird. Thanks to Federico Caselli for lots of work
- on this one.
-
- .. seealso::
-
- :ref:`computed_ddl`
-
-
- .. change::
- :tags: bug, engine, postgresql
- :tickets: 4955
-
- Fixed bug in :class:`_reflection.Inspector` where the cache key generation did not
- take into account arguments passed in the form of tuples, such as the tuple
- of view name styles to return for the PostgreSQL dialect. This would lead
- the inspector to cache too generally for a more specific set of criteria.
- The logic has been adjusted to include every keyword element in the cache,
- as every argument is expected to be appropriate for a cache else the
- caching decorator should be bypassed by the dialect.
-
-
- .. change::
- :tags: bug, mssql
- :tickets: 4923
-
- Fixed an issue in the :meth:`_engine.Engine.table_names` method where it would
- feed the dialect's default schema name back into the dialect level table
- function, which in the case of SQL Server would interpret it as a
- dot-tokenized schema name as viewed by the mssql dialect, which would
- cause the method to fail in the case where the database username actually
- had a dot inside of it. In 1.3, this method is still used by the
- :meth:`_schema.MetaData.reflect` function so is a prominent codepath. In 1.4,
- which is the current master development branch, this issue doesn't exist,
- both because :meth:`_schema.MetaData.reflect` isn't using this method nor does the
- method pass the default schema name explicitly. The fix nonetheless
- guards against the default server name value returned by the dialect from
- being interpreted as dot-tokenized name under any circumstances by
- wrapping it in quoted_name().
-
- .. change::
- :tags: bug, orm
- :tickets: 4974
-
- Fixed ORM bug where a "secondary" table that referred to a selectable which
- in some way would refer to the local primary table would apply aliasing to
- both sides of the join condition when a relationship-related join, either
- via :meth:`_query.Query.join` or by :func:`_orm.joinedload`, were generated. The
- "local" side is now excluded.
-
- .. change::
- :tags: usecase, sql
- :tickets: 4276
-
- Added new accessors to expressions of type :class:`_types.JSON` to allow for
- specific datatype access and comparison, covering strings, integers,
- numeric, boolean elements. This revises the documented approach of
- CASTing to string when comparing values, instead adding specific
- functionality into the PostgreSQL, SQlite, MySQL dialects to reliably
- deliver these basic types in all cases.
-
- .. seealso::
-
- :class:`_types.JSON`
-
- :meth:`_sqltypes.JSON.Comparator.as_string`
-
- :meth:`_sqltypes.JSON.Comparator.as_boolean`
-
- :meth:`_sqltypes.JSON.Comparator.as_float`
-
- :meth:`_sqltypes.JSON.Comparator.as_integer`
-
- .. change::
- :tags: usecase, oracle
- :tickets: 4799
-
- Added dialect-level flag ``encoding_errors`` to the cx_Oracle dialect,
- which can be specified as part of :func:`_sa.create_engine`. This is passed
- to SQLAlchemy's unicode decoding converter under Python 2, and to
- cx_Oracle's ``cursor.var()`` object as the ``encodingErrors`` parameter
- under Python 3, for the very unusual case that broken encodings are present
- in the target database which cannot be fetched unless error handling is
- relaxed. The value is ultimately one of the Python "encoding errors"
- parameters passed to ``decode()``.
-
- .. change::
- :tags: usecase, sql
- :tickets: 4933
-
- The :func:`_expression.text` construct now supports "unique" bound parameters, which
- will dynamically uniquify themselves on compilation thus allowing multiple
- :func:`_expression.text` constructs with the same bound parameter names to be combined
- together.
-
-
- .. change::
- :tags: bug, oracle
- :tickets: 4913
-
- The :class:`_types.NCHAR` datatype will now bind to the
- ``cx_Oracle.FIXED_NCHAR`` DBAPI data bindings when used in a bound
- parameter, which supplies proper comparison behavior against a
- variable-length string. Previously, the :class:`_types.NCHAR` datatype
- would bind to ``cx_oracle.NCHAR`` which is not fixed length; the
- :class:`_types.CHAR` datatype already binds to ``cx_Oracle.FIXED_CHAR``
- so it is now consistent that :class:`_types.NCHAR` binds to
- ``cx_Oracle.FIXED_NCHAR``.
-
-
-
- .. change::
- :tags: bug, firebird
- :tickets: 4903
-
- Added additional "disconnect" message "Error writing data to the
- connection" to Firebird disconnection detection. Pull request courtesy
- lukens.
-
-.. changelog::
- :version: 1.3.10
- :released: October 9, 2019
-
- .. change::
- :tags: bug, mssql
- :tickets: 4857
-
- Fixed bug in SQL Server dialect with new "max_identifier_length" feature
- where the mssql dialect already featured this flag, and the implementation
- did not accommodate for the new initialization hook correctly.
-
-
- .. change::
- :tags: bug, oracle
- :tickets: 4898, 4857
-
- Fixed regression in Oracle dialect that was inadvertently using max
- identifier length of 128 characters on Oracle server 12.2 and greater even
- though the stated contract for the remainder of the 1.3 series is that
- this value stays at 30 until version SQLAlchemy 1.4. Also repaired issues
- with the retrieval of the "compatibility" version, and removed the warning
- emitted when the "v$parameter" view was not accessible as this was causing
- user confusion.
-
-.. changelog::
- :version: 1.3.9
- :released: October 4, 2019
-
- .. change::
- :tags: usecase, engine
- :tickets: 4857
-
- Added new :func:`_sa.create_engine` parameter
- :paramref:`_sa.create_engine.max_identifier_length`. This overrides the
- dialect-coded "max identifier length" in order to accommodate for databases
- that have recently changed this length and the SQLAlchemy dialect has
- not yet been adjusted to detect for that version. This parameter interacts
- with the existing :paramref:`_sa.create_engine.label_length` parameter in that
- it establishes the maximum (and default) value for anonymously generated
- labels. Additionally, post-connection detection of max identifier lengths
- has been added to the dialect system. This feature is first being used
- by the Oracle dialect.
-
- .. seealso::
-
- :ref:`oracle_max_identifier_lengths` - in the Oracle dialect documentation
-
- .. change::
- :tags: usecase, oracle
- :tickets: 4857
-
- The Oracle dialect now emits a warning if Oracle version 12.2 or greater is
- used, and the :paramref:`_sa.create_engine.max_identifier_length` parameter is
- not set. The version in this specific case defaults to that of the
- "compatibility" version set in the Oracle server configuration, not the
- actual server version. In version 1.4, the default max_identifier_length
- for 12.2 or greater will move to 128 characters. In order to maintain
- forwards compatibility, applications should set
- :paramref:`_sa.create_engine.max_identifier_length` to 30 in order to maintain
- the same length behavior, or to 128 in order to test the upcoming behavior.
- This length determines among other things how generated constraint names
- are truncated for statements like ``CREATE CONSTRAINT`` and ``DROP
- CONSTRAINT``, which means a the new length may produce a name-mismatch
- against a name that was generated with the old length, impacting database
- migrations.
-
- .. seealso::
-
- :ref:`oracle_max_identifier_lengths` - in the Oracle dialect documentation
-
- .. change::
- :tags: usecase, sqlite
- :tickets: 4863
-
- Added support for sqlite "URI" connections, which allow for sqlite-specific
- flags to be passed in the query string such as "read only" for Python
- sqlite3 drivers that support this.
-
- .. seealso::
-
- :ref:`pysqlite_uri_connections`
-
- .. change::
- :tags: bug, tests
- :tickets: 4285
-
- Fixed unit test regression released in 1.3.8 that would cause failure for
- Oracle, SQL Server and other non-native ENUM platforms due to new
- enumeration tests added as part of :ticket:`4285` enum sortability in the
- unit of work; the enumerations created constraints that were duplicated on
- name.
-
- .. change::
- :tags: bug, oracle
- :tickets: 4886
-
- Restored adding cx_Oracle.DATETIME to the setinputsizes() call when a
- SQLAlchemy :class:`.Date`, :class:`.DateTime` or :class:`.Time` datatype is
- used, as some complex queries require this to be present. This was removed
- in the 1.2 series for arbitrary reasons.
-
- .. change::
- :tags: bug, mssql
- :tickets: 4883
-
- Added identifier quoting to the schema name applied to the "use" statement
- which is invoked when a SQL Server multipart schema name is used within a
- :class:`_schema.Table` that is being reflected, as well as for :class:`_reflection.Inspector`
- methods such as :meth:`_reflection.Inspector.get_table_names`; this accommodates for
- special characters or spaces in the database name. Additionally, the "use"
- statement is not emitted if the current database matches the target owner
- database name being passed.
-
- .. change::
- :tags: bug, orm
- :tickets: 4872
-
- Fixed regression in selectinload loader strategy caused by :ticket:`4775`
- (released in version 1.3.6) where a many-to-one attribute of None would no
- longer be populated by the loader. While this was usually not noticeable
- due to the lazyloader populating None upon get, it would lead to a detached
- instance error if the object were detached.
-
- .. change::
- :tags: bug, orm
- :tickets: 4873
-
- Passing a plain string expression to :meth:`.Session.query` is deprecated,
- as all string coercions were removed in :ticket:`4481` and this one should
- have been included. The :func:`_expression.literal_column` function may be used to
- produce a textual column expression.
-
- .. change::
- :tags: usecase, sql
- :tickets: 4847
-
- Added an explicit error message for the case when objects passed to
- :class:`_schema.Table` are not :class:`.SchemaItem` objects, rather than resolving
- to an attribute error.
-
-
- .. change::
- :tags: bug, orm
- :tickets: 4890
-
- A warning is emitted for a condition in which the :class:`.Session` may
- implicitly swap an object out of the identity map for another one with the
- same primary key, detaching the old one, which can be an observed result of
- load operations which occur within the :meth:`.SessionEvents.after_flush`
- hook. The warning is intended to notify the user that some special
- condition has caused this to happen and that the previous object may not be
- in the expected state.
-
- .. change::
- :tags: bug, sql
- :tickets: 4837
-
- Characters that interfere with "pyformat" or "named" formats in bound
- parameters, namely ``%, (, )`` and the space character, as well as a few
- other typically undesirable characters, are stripped early for a
- :func:`.bindparam` that is using an anonymized name, which is typically
- generated automatically from a named column which itself includes these
- characters in its name and does not use a ``.key``, so that they do not
- interfere either with the SQLAlchemy compiler's use of string formatting or
- with the driver-level parsing of the parameter, both of which could be
- demonstrated before the fix. The change only applies to anonymized
- parameter names that are generated and consumed internally, not end-user
- defined names, so the change should have no impact on any existing code.
- Applies in particular to the psycopg2 driver which does not otherwise quote
- special parameter names, but also strips leading underscores to suit Oracle
- (but not yet leading numbers, as some anon parameters are currently
- entirely numeric/underscore based); Oracle in any case continues to quote
- parameter names that include special characters.
-
-.. changelog::
- :version: 1.3.8
- :released: August 27, 2019
-
- .. change::
- :tags: bug, orm
- :tickets: 4823
-
- Fixed bug where :class:`_orm.Load` objects were not pickleable due to
- mapper/relationship state in the internal context dictionary. These
- objects are now converted to picklable using similar techniques as that of
- other elements within the loader option system that have long been
- serializable.
-
- .. change::
- :tags: bug, postgresql
- :tickets: 4623
-
- Revised the approach for the just added support for the psycopg2
- "execute_values()" feature added in 1.3.7 for :ticket:`4623`. The approach
- relied upon a regular expression that would fail to match for a more
- complex INSERT statement such as one which had subqueries involved. The
- new approach matches exactly the string that was rendered as the VALUES
- clause.
-
- .. change::
- :tags: usecase, orm
- :tickets: 4285
-
- Added support for the use of an :class:`.Enum` datatype using Python
- pep-435 enumeration objects as values for use as a primary key column
- mapped by the ORM. As these values are not inherently sortable, as
- required by the ORM for primary keys, a new
- :attr:`.TypeEngine.sort_key_function` attribute is added to the typing
- system which allows any SQL type to implement a sorting for Python objects
- of its type which is consulted by the unit of work. The :class:`.Enum`
- type then defines this using the database value of a given enumeration.
- The sorting scheme can be also be redefined by passing a callable to the
- :paramref:`.Enum.sort_key_function` parameter. Pull request courtesy
- Nicolas Caniart.
-
- .. change::
- :tags: bug, engine
- :tickets: 4807
-
- Fixed an issue whereby if the dialect "initialize" process which occurs on
- first connect would encounter an unexpected exception, the initialize
- process would fail to complete and then no longer attempt on subsequent
- connection attempts, leaving the dialect in an un-initialized, or partially
- initialized state, within the scope of parameters that need to be
- established based on inspection of a live connection. The "invoke once"
- logic in the event system has been reworked to accommodate for this
- occurrence using new, private API features that establish an "exec once"
- hook that will continue to allow the initializer to fire off on subsequent
- connections, until it completes without raising an exception. This does not
- impact the behavior of the existing ``once=True`` flag within the event
- system.
-
- .. change::
- :tags: bug, sqlite, reflection
- :tickets: 4810
-
- Fixed bug where a FOREIGN KEY that was set up to refer to the parent table
- by table name only without the column names would not correctly be
- reflected as far as setting up the "referred columns", since SQLite's
- PRAGMA does not report on these columns if they weren't given explicitly.
- For some reason this was hardcoded to assume the name of the local column,
- which might work for some cases but is not correct. The new approach
- reflects the primary key of the referred table and uses the constraint
- columns list as the referred columns list, if the remote column(s) aren't
- present in the reflected pragma directly.
-
-
- .. change::
- :tags: bug, postgresql
- :tickets: 4822
-
- Fixed bug where Postgresql operators such as
- :meth:`.postgresql.ARRAY.Comparator.contains` and
- :meth:`.postgresql.ARRAY.Comparator.contained_by` would fail to function
- correctly for non-integer values when used against a
- :class:`_postgresql.array` object, due to an erroneous assert statement.
-
- .. change::
- :tags: feature, engine
- :tickets: 4815
-
- Added new parameter :paramref:`_sa.create_engine.hide_parameters` which when
- set to True will cause SQL parameters to no longer be logged, nor rendered
- in the string representation of a :class:`.StatementError` object.
-
-
- .. change::
- :tags: usecase, postgresql
- :tickets: 4824
-
- Added support for reflection of CHECK constraints that include the special
- PostgreSQL qualifier "NOT VALID", which can be present for CHECK
- constraints that were added to an existing table with the directive that
- they not be applied to existing data in the table. The PostgreSQL
- dictionary for CHECK constraints as returned by
- :meth:`_reflection.Inspector.get_check_constraints` may include an additional entry
- ``dialect_options`` which within will contain an entry ``"not_valid":
- True`` if this symbol is detected. Pull request courtesy Bill Finn.
-
-.. changelog::
- :version: 1.3.7
- :released: August 14, 2019
-
- .. change::
- :tags: bug, sql
- :tickets: 4778
-
- Fixed issue where :class:`.Index` object which contained a mixture of
- functional expressions which were not resolvable to a particular column,
- in combination with string-based column names, would fail to initialize
- its internal state correctly leading to failures during DDL compilation.
-
- .. change::
- :tags: bug, sqlite
- :tickets: 4798
-
- The dialects that support json are supposed to take arguments
- ``json_serializer`` and ``json_deserializer`` at the create_engine() level,
- however the SQLite dialect calls them ``_json_serializer`` and
- ``_json_deserilalizer``. The names have been corrected, the old names are
- accepted with a change warning, and these parameters are now documented as
- :paramref:`_sa.create_engine.json_serializer` and
- :paramref:`_sa.create_engine.json_deserializer`.
-
-
- .. change::
- :tags: bug, mysql
- :tickets: 4804
-
- The MySQL dialects will emit "SET NAMES" at the start of a connection when
- charset is given to the MySQL driver, to appease an apparent behavior
- observed in MySQL 8.0 that raises a collation error when a UNION includes
- string columns unioned against columns of the form CAST(NULL AS CHAR(..)),
- which is what SQLAlchemy's polymorphic_union function does. The issue
- seems to have affected PyMySQL for at least a year, however has recently
- appeared as of mysqlclient 1.4.4 based on changes in how this DBAPI creates
- a connection. As the presence of this directive impacts three separate
- MySQL charset settings which each have intricate effects based on their
- presence, SQLAlchemy will now emit the directive on new connections to
- ensure correct behavior.
-
- .. change::
- :tags: usecase, postgresql
- :tickets: 4623
-
- Added new dialect flag for the psycopg2 dialect, ``executemany_mode`` which
- supersedes the previous experimental ``use_batch_mode`` flag.
- ``executemany_mode`` supports both the "execute batch" and "execute values"
- functions provided by psycopg2, the latter which is used for compiled
- :func:`_expression.insert` constructs. Pull request courtesy Yuval Dinari.
-
- .. seealso::
-
- :ref:`psycopg2_executemany_mode`
-
-
-
-
- .. change::
- :tags: bug, sql
- :tickets: 4787
-
- Fixed bug where :meth:`.TypeEngine.column_expression` method would not be
- applied to subsequent SELECT statements inside of a UNION or other
- :class:`_selectable.CompoundSelect`, even though the SELECT statements are rendered at
- the topmost level of the statement. New logic now differentiates between
- rendering the column expression, which is needed for all SELECTs in the
- list, vs. gathering the returned data type for the result row, which is
- needed only for the first SELECT.
-
- .. change::
- :tags: bug, sqlite
- :tickets: 4793
-
- Fixed bug where usage of "PRAGMA table_info" in SQLite dialect meant that
- reflection features to detect for table existence, list of table columns,
- and list of foreign keys, would default to any table in any attached
- database, when no schema name was given and the table did not exist in the
- base schema. The fix explicitly runs PRAGMA for the 'main' schema and then
- the 'temp' schema if the 'main' returned no rows, to maintain the behavior
- of tables + temp tables in the "no schema" namespace, attached tables only
- in the "schema" namespace.
-
-
- .. change::
- :tags: bug, sql
- :tickets: 4780
-
- Fixed issue where internal cloning of SELECT constructs could lead to a key
- error if the copy of the SELECT changed its state such that its list of
- columns changed. This was observed to be occurring in some ORM scenarios
- which may be unique to 1.3 and above, so is partially a regression fix.
-
-
-
- .. change::
- :tags: bug, orm
- :tickets: 4777
-
- Fixed regression caused by new selectinload for many-to-one logic where
- a primaryjoin condition not based on real foreign keys would cause
- KeyError if a related object did not exist for a given key value on the
- parent object.
-
- .. change::
- :tags: usecase, mysql
- :tickets: 4783
-
- Added reserved words ARRAY and MEMBER to the MySQL reserved words list, as
- MySQL 8.0 has now made these reserved.
-
-
- .. change::
- :tags: bug, events
- :tickets: 4794
-
- Fixed issue in event system where using the ``once=True`` flag with
- dynamically generated listener functions would cause event registration of
- future events to fail if those listener functions were garbage collected
- after they were used, due to an assumption that a listened function is
- strongly referenced. The "once" wrapped is now modified to strongly
- reference the inner function persistently, and documentation is updated
- that using "once" does not imply automatic de-registration of listener
- functions.
-
- .. change::
- :tags: bug, mysql
- :tickets: 4751
-
- Added another fix for an upstream MySQL 8 issue where a case sensitive
- table name is reported incorrectly in foreign key constraint reflection,
- this is an extension of the fix first added for :ticket:`4344` which
- affects a case sensitive column name. The new issue occurs through MySQL
- 8.0.17, so the general logic of the 88718 fix remains in place.
-
- .. seealso::
-
- https://bugs.mysql.com/bug.php?id=96365 - upstream bug
-
-
- .. change::
- :tags: usecase, mssql
- :tickets: 4782
-
- Added new :func:`_mssql.try_cast` construct for SQL Server which emits
- "TRY_CAST" syntax. Pull request courtesy Leonel Atencio.
-
- .. change::
- :tags: bug, orm
- :tickets: 4803
-
- Fixed bug where using :meth:`_query.Query.first` or a slice expression in
- conjunction with a query that has an expression based "offset" applied
- would raise TypeError, due to an "or" conditional against "offset" that did
- not expect it to be a SQL expression as opposed to an integer or None.
-
-
-.. changelog::
- :version: 1.3.6
- :released: July 21, 2019
-
- .. change::
- :tags: bug, engine
- :tickets: 4754
-
- Fixed bug where using reflection function such as :meth:`_schema.MetaData.reflect`
- with an :class:`_engine.Engine` object that had execution options applied to it
- would fail, as the resulting :class:`.OptionEngine` proxy object failed to
- include a ``.engine`` attribute used within the reflection routines.
-
- .. change::
- :tags: bug, mysql
- :tickets: 4743
-
- Fixed bug where the special logic to render "NULL" for the
- :class:`_types.TIMESTAMP` datatype when ``nullable=True`` would not work if the
- column's datatype were a :class:`.TypeDecorator` or a :class:`.Variant`.
- The logic now ensures that it unwraps down to the original
- :class:`_types.TIMESTAMP` so that this special case NULL keyword is correctly
- rendered when requested.
-
- .. change::
- :tags: performance, orm
- :tickets: 4775
-
- The optimization applied to selectin loading in :ticket:`4340` where a JOIN
- is not needed to eagerly load related items is now applied to many-to-one
- relationships as well, so that only the related table is queried for a
- simple join condition. In this case, the related items are queried
- based on the value of a foreign key column on the parent; if these columns
- are deferred or otherwise not loaded on any of the parent objects in
- the collection, the loader falls back to the JOIN method.
-
-
- .. change::
- :tags: bug, orm
- :tickets: 4773
-
- Fixed regression caused by :ticket:`4365` where a join from an entity to
- itself without using aliases no longer raises an informative error message,
- instead failing on an assertion. The informative error condition has been
- restored.
-
-
- .. change::
- :tags: orm, feature
- :tickets: 4736
-
- Added new loader option method :meth:`_orm.Load.options` which allows loader
- options to be constructed hierarchically, so that many sub-options can be
- applied to a particular path without needing to call :func:`.defaultload`
- many times. Thanks to Alessio Bogon for the idea.
-
-
- .. change::
- :tags: usecase, postgresql
- :tickets: 4771
-
- Added support for reflection of indexes on PostgreSQL partitioned tables,
- which was added to PostgreSQL as of version 11.
-
- .. change::
- :tags: bug, mysql
- :tickets: 4624
-
- Enhanced MySQL/MariaDB version string parsing to accommodate for exotic
- MariaDB version strings where the "MariaDB" word is embedded among other
- alphanumeric characters such as "MariaDBV1". This detection is critical in
- order to correctly accommodate for API features that have split between MySQL
- and MariaDB such as the "transaction_isolation" system variable.
-
-
- .. change::
- :tags: bug, mssql
- :tickets: 4745
-
- Ensured that the queries used to reflect indexes and view definitions will
- explicitly CAST string parameters into NVARCHAR, as many SQL Server drivers
- frequently treat string values, particularly those with non-ascii
- characters or larger string values, as TEXT which often don't compare
- correctly against VARCHAR characters in SQL Server's information schema
- tables for some reason. These CAST operations already take place for
- reflection queries against SQL Server ``information_schema.`` tables but
- were missing from three additional queries that are against ``sys.``
- tables.
-
- .. change::
- :tags: bug, orm
- :tickets: 4713
-
- Fixed an issue where the :meth:`.orm._ORMJoin.join` method, which is a
- not-internally-used ORM-level method that exposes what is normally an
- internal process of :meth:`_query.Query.join`, did not propagate the ``full`` and
- ``outerjoin`` keyword arguments correctly. Pull request courtesy Denis
- Kataev.
-
- .. change::
- :tags: bug, sql
- :tickets: 4758
-
- Adjusted the initialization for :class:`.Enum` to minimize how often it
- invokes the ``.__members__`` attribute of a given PEP-435 enumeration
- object, to suit the case where this attribute is expensive to invoke, as is
- the case for some popular third party enumeration libraries.
-
-
- .. change::
- :tags: bug, orm
- :tickets: 4772
-
- Fixed bug where a many-to-one relationship that specified ``uselist=True``
- would fail to update correctly during a primary key change where a related
- column needs to change.
-
-
- .. change::
- :tags: bug, orm
- :tickets: 4772
-
- Fixed bug where the detection for many-to-one or one-to-one use with a
- "dynamic" relationship, which is an invalid configuration, would fail to
- raise if the relationship were configured with ``uselist=True``. The
- current fix is that it warns, instead of raises, as this would otherwise be
- backwards incompatible, however in a future release it will be a raise.
-
-
- .. change::
- :tags: bug, orm
- :tickets: 4767
-
- Fixed bug where a synonym created against a mapped attribute that does not
- exist yet, as is the case when it refers to backref before mappers are
- configured, would raise recursion errors when trying to test for attributes
- on it which ultimately don't exist (as occurs when the classes are run
- through Sphinx autodoc), as the unconfigured state of the synonym would put
- it into an attribute not found loop.
-
-
- .. change::
- :tags: usecase, postgresql
- :tickets: 4756
-
- Added support for multidimensional Postgresql array literals via nesting
- the :class:`_postgresql.array` object within another one. The
- multidimensional array type is detected automatically.
-
- .. seealso::
-
- :class:`_postgresql.array`
-
- .. change::
- :tags: bug, sql, postgresql
- :tickets: 4760
-
- Fixed issue where the :class:`_functions.array_agg` construct in combination with
- :meth:`.FunctionElement.filter` would not produce the correct operator
- precedence in combination with the array index operator.
-
-
- .. change::
- :tags: bug, sql
- :tickets: 4747
-
- Fixed an unlikely issue where the "corresponding column" routine for unions
- and other :class:`_selectable.CompoundSelect` objects could return the wrong column in
- some overlapping column situations, thus potentially impacting some ORM
- operations when set operations are in use, if the underlying
- :func:`_expression.select` constructs were used previously in other similar kinds of
- routines, due to a cached value not being cleared.
-
- .. change::
- :tags: usecase, sqlite
- :tickets: 4766
-
- Added support for composite (tuple) IN operators with SQLite, by rendering
- the VALUES keyword for this backend. As other backends such as DB2 are
- known to use the same syntax, the syntax is enabled in the base compiler
- using a dialect-level flag ``tuple_in_values``. The change also includes
- support for "empty IN tuple" expressions for SQLite when using "in_()"
- between a tuple value and an empty set.
-
-
-.. changelog::
- :version: 1.3.5
- :released: June 17, 2019
-
- .. change::
- :tags: bug, mysql
- :tickets: 4715
-
- Fixed bug where MySQL ON DUPLICATE KEY UPDATE would not accommodate setting
- a column to the value NULL. Pull request courtesy Lukáš Banič.
-
- .. change::
- :tags: bug, orm
- :tickets: 4723
-
- Fixed a series of related bugs regarding joined table inheritance more than
- two levels deep, in conjunction with modification to primary key values,
- where those primary key columns are also linked together in a foreign key
- relationship as is typical for joined table inheritance. The intermediary
- table in a three-level inheritance hierarchy will now get its UPDATE if
- only the primary key value has changed and passive_updates=False (e.g.
- foreign key constraints not being enforced), whereas before it would be
- skipped; similarly, with passive_updates=True (e.g. ON UPDATE CASCADE in
- effect), the third-level table will not receive an UPDATE statement as was
- the case earlier which would fail since CASCADE already modified it. In a
- related issue, a relationship linked to a three-level inheritance hierarchy
- on the primary key of an intermediary table of a joined-inheritance
- hierarchy will also correctly have its foreign key column updated when the
- parent object's primary key is modified, even if that parent object is a
- subclass of the linked parent class, whereas before these classes would
- not be counted.
-
- .. change::
- :tags: bug, orm
- :tickets: 4729
-
- Fixed bug where the :attr:`_orm.Mapper.all_orm_descriptors` accessor would
- return an entry for the :class:`_orm.Mapper` itself under the declarative
- ``__mapper__`` key, when this is not a descriptor. The ``.is_attribute``
- flag that's present on all :class:`.InspectionAttr` objects is now
- consulted, which has also been modified to be ``True`` for an association
- proxy, as it was erroneously set to False for this object.
-
- .. change::
- :tags: bug, orm
- :tickets: 4704
-
- Fixed regression in :meth:`_query.Query.join` where the ``aliased=True`` flag
- would not properly apply clause adaptation to filter criteria, if a
- previous join were made to the same entity. This is because the adapters
- were placed in the wrong order. The order has been reversed so that the
- adapter for the most recent ``aliased=True`` call takes precedence as was
- the case in 1.2 and earlier. This broke the "elementtree" examples among
- other things.
-
- .. change::
- :tags: bug, orm, py3k
- :tickets: 4674
-
- Replaced the Python compatibility routines for ``getfullargspec()`` with a
- fully vendored version from Python 3.3. Originally, Python was emitting
- deprecation warnings for this function in Python 3.8 alphas. While this
- change was reverted, it was observed that Python 3 implementations for
- ``getfullargspec()`` are an order of magnitude slower as of the 3.4 series
- where it was rewritten against ``Signature``. While Python plans to
- improve upon this situation, SQLAlchemy projects for now are using a simple
- replacement to avoid any future issues.
-
- .. change::
- :tags: bug, orm
- :tickets: 4694
-
- Reworked the attribute mechanics used by :class:`.AliasedClass` to no
- longer rely upon calling ``__getattribute__`` on the MRO of the wrapped
- class, and to instead resolve the attribute normally on the wrapped class
- using getattr(), and then unwrap/adapt that. This allows a greater range
- of attribute styles on the mapped class including special ``__getattr__()``
- schemes; but it also makes the code simpler and more resilient in general.
-
- .. change::
- :tags: usecase, postgresql
- :tickets: 4717
-
- Added support for column sorting flags when reflecting indexes for
- PostgreSQL, including ASC, DESC, NULLSFIRST, NULLSLAST. Also adds this
- facility to the reflection system in general which can be applied to other
- dialects in future releases. Pull request courtesy Eli Collins.
-
- .. change::
- :tags: bug, postgresql
- :tickets: 4701
-
- Fixed bug where PostgreSQL dialect could not correctly reflect an ENUM
- datatype that has no members, returning a list with ``None`` for the
- ``get_enums()`` call and raising a TypeError when reflecting a column which
- has such a datatype. The inspection now returns an empty list.
-
- .. change::
- :tags: bug, sql
- :tickets: 4730
-
- Fixed a series of quoting issues which all stemmed from the concept of the
- :func:`_expression.literal_column` construct, which when being "proxied" through a
- subquery to be referred towards by a label that matches its text, the label
- would not have quoting rules applied to it, even if the string in the
- :class:`.Label` were set up as a :class:`.quoted_name` construct. Not
- applying quoting to the text of the :class:`.Label` is a bug because this
- text is strictly a SQL identifier name and not a SQL expression, and the
- string should not have quotes embedded into it already unlike the
- :func:`_expression.literal_column` which it may be applied towards. The existing
- behavior of a non-labeled :func:`_expression.literal_column` being propagated as is on
- the outside of a subquery is maintained in order to help with manual
- quoting schemes, although it's not clear if valid SQL can be generated for
- such a construct in any case.
-
-.. changelog::
- :version: 1.3.4
- :released: May 27, 2019
-
- .. change::
- :tags: feature, mssql
- :tickets: 4657
-
- Added support for SQL Server filtered indexes, via the ``mssql_where``
- parameter which works similarly to that of the ``postgresql_where`` index
- function in the PostgreSQL dialect.
-
- .. seealso::
-
- :ref:`mssql_index_where`
-
- .. change::
- :tags: bug, misc
- :tickets: 4625
-
- Removed errant "sqla_nose.py" symbol from MANIFEST.in which created an
- undesirable warning message.
-
- .. change::
- :tags: bug, sql
- :tickets: 4653
-
- Fixed that the :class:`.GenericFunction` class was inadvertently
- registering itself as one of the named functions. Pull request courtesy
- Adrien Berchet.
-
- .. change::
- :tags: bug, engine, postgresql
- :tickets: 4663
-
- Moved the "rollback" which occurs during dialect initialization so that it
- occurs after additional dialect-specific initialize steps, in particular
- those of the psycopg2 dialect which would inadvertently leave transactional
- state on the first new connection, which could interfere with some
- psycopg2-specific APIs which require that no transaction is started. Pull
- request courtesy Matthew Wilkes.
-
-
- .. change::
- :tags: bug, orm
- :tickets: 4695
-
- Fixed issue where the :paramref:`.AttributeEvents.active_history` flag
- would not be set for an event listener that propagated to a subclass via the
- :paramref:`.AttributeEvents.propagate` flag. This bug has been present
- for the full span of the :class:`.AttributeEvents` system.
-
-
- .. change::
- :tags: bug, orm
- :tickets: 4690
-
- Fixed regression where new association proxy system was still not proxying
- hybrid attributes when they made use of the ``@hybrid_property.expression``
- decorator to return an alternate SQL expression, or when the hybrid
- returned an arbitrary :class:`.PropComparator`, at the expression level.
- This involved further generalization of the heuristics used to detect the
- type of object being proxied at the level of :class:`.QueryableAttribute`,
- to better detect if the descriptor ultimately serves mapped classes or
- column expressions.
-
- .. change::
- :tags: bug, orm
- :tickets: 4686
-
- Applied the mapper "configure mutex" against the declarative class mapping
- process, to guard against the race which can occur if mappers are used
- while dynamic module import schemes are still in the process of configuring
- mappers for related classes. This does not guard against all possible race
- conditions, such as if the concurrent import has not yet encountered the
- dependent classes as of yet, however it guards against as much as possible
- within the SQLAlchemy declarative process.
-
- .. change::
- :tags: bug, mssql
- :tickets: 4680
-
- Added error code 20047 to "is_disconnect" for pymssql. Pull request
- courtesy Jon Schuff.
-
-
- .. change::
- :tags: bug, postgresql, orm
- :tickets: 4661
-
- Fixed an issue where the "number of rows matched" warning would emit even if
- the dialect reported "supports_sane_multi_rowcount=False", as is the case
- for psycogp2 with ``use_batch_mode=True`` and others.
-
-
- .. change::
- :tags: bug, sql
- :tickets: 4618
-
- Fixed issue where double negation of a boolean column wouldn't reset
- the "NOT" operator.
-
- .. change::
- :tags: mysql, bug
- :tickets: 4650
-
- Added support for DROP CHECK constraint which is required by MySQL 8.0.16
- to drop a CHECK constraint; MariaDB supports plain DROP CONSTRAINT. The
- logic distinguishes between the two syntaxes by checking the server version
- string for MariaDB presence. Alembic migrations has already worked
- around this issue by implementing its own DROP for MySQL / MariaDB CHECK
- constraints, however this change implements it straight in Core so that its
- available for general use. Pull request courtesy Hannes Hansen.
-
- .. change::
- :tags: bug, orm
- :tickets: 4647
-
- A warning is now emitted for the case where a transient object is being
- merged into the session with :meth:`.Session.merge` when that object is
- already transient in the :class:`.Session`. This warns for the case where
- the object would normally be double-inserted.
-
-
- .. change::
- :tags: bug, orm
- :tickets: 4676
-
- Fixed regression in new relationship m2o comparison logic first introduced
- at :ref:`change_4359` when comparing to an attribute that is persisted as
- NULL and is in an un-fetched state in the mapped instance. Since the
- attribute has no explicit default, it needs to default to NULL when
- accessed in a persistent setting.
-
-
- .. change::
- :tags: bug, sql
- :tickets: 4569
-
- The :class:`.GenericFunction` namespace is being migrated so that function
- names are looked up in a case-insensitive manner, as SQL functions do not
- collide on case sensitive differences nor is this something which would
- occur with user-defined functions or stored procedures. Lookups for
- functions declared with :class:`.GenericFunction` now use a case
- insensitive scheme, however a deprecation case is supported which allows
- two or more :class:`.GenericFunction` objects with the same name of
- different cases to exist, which will cause case sensitive lookups to occur
- for that particular name, while emitting a warning at function registration
- time. Thanks to Adrien Berchet for a lot of work on this complicated
- feature.
-
-
-.. changelog::
- :version: 1.3.3
- :released: April 15, 2019
-
- .. change::
- :tags: bug, postgresql
- :tickets: 4601
-
- Fixed regression from release 1.3.2 caused by :ticket:`4562` where a URL
- that contained only a query string and no hostname, such as for the
- purposes of specifying a service file with connection information, would no
- longer be propagated to psycopg2 properly. The change in :ticket:`4562`
- has been adjusted to further suit psycopg2's exact requirements, which is
- that if there are any connection parameters whatsoever, the "dsn" parameter
- is no longer required, so in this case the query string parameters are
- passed alone.
-
- .. change::
- :tags: bug, pool
- :tickets: 4585
-
- Fixed behavioral regression as a result of deprecating the "use_threadlocal"
- flag for :class:`_pool.Pool`, where the :class:`.SingletonThreadPool` no longer
- makes use of this option which causes the "rollback on return" logic to take
- place when the same :class:`_engine.Engine` is used multiple times in the context
- of a transaction to connect or implicitly execute, thereby cancelling the
- transaction. While this is not the recommended way to work with engines
- and connections, it is nonetheless a confusing behavioral change as when
- using :class:`.SingletonThreadPool`, the transaction should stay open
- regardless of what else is done with the same engine in the same thread.
- The ``use_threadlocal`` flag remains deprecated however the
- :class:`.SingletonThreadPool` now implements its own version of the same
- logic.
-
-
- .. change::
- :tags: bug, orm
- :tickets: 4584
-
- Fixed 1.3 regression in new "ambiguous FROMs" query logic introduced in
- :ref:`change_4365` where a :class:`_query.Query` that explicitly places an entity
- in the FROM clause with :meth:`_query.Query.select_from` and also joins to it
- using :meth:`_query.Query.join` would later cause an "ambiguous FROM" error if
- that entity were used in additional joins, as the entity appears twice in
- the "from" list of the :class:`_query.Query`. The fix resolves this ambiguity by
- folding the standalone entity into the join that it's already a part of in
- the same way that ultimately happens when the SELECT statement is rendered.
-
- .. change::
- :tags: bug, ext
- :tickets: 4603
-
- Fixed bug where using ``copy.copy()`` or ``copy.deepcopy()`` on
- :class:`.MutableList` would cause the items within the list to be
- duplicated, due to an inconsistency in how Python pickle and copy both make
- use of ``__getstate__()`` and ``__setstate__()`` regarding lists. In order
- to resolve, a ``__reduce_ex__`` method had to be added to
- :class:`.MutableList`. In order to maintain backwards compatibility with
- existing pickles based on ``__getstate__()``, the ``__setstate__()`` method
- remains as well; the test suite asserts that pickles made against the old
- version of the class can still be deserialized by the pickle module.
-
- .. change::
- :tags: bug, orm
- :tickets: 4606
-
- Adjusted the :meth:`_query.Query.filter_by` method to not call :func:`.and()`
- internally against multiple criteria, instead passing it off to
- :meth:`_query.Query.filter` as a series of criteria, instead of a single criteria.
- This allows :meth:`_query.Query.filter_by` to defer to :meth:`_query.Query.filter`'s
- treatment of variable numbers of clauses, including the case where the list
- is empty. In this case, the :class:`_query.Query` object will not have a
- ``.whereclause``, which allows subsequent "no whereclause" methods like
- :meth:`_query.Query.select_from` to behave consistently.
-
- .. change::
- :tags: bug, mssql
- :tickets: 4587
-
- Fixed issue in SQL Server dialect where if a bound parameter were present in
- an ORDER BY expression that would ultimately not be rendered in the SQL
- Server version of the statement, the parameters would still be part of the
- execution parameters, leading to DBAPI-level errors. Pull request courtesy
- Matt Lewellyn.
-
-.. changelog::
- :version: 1.3.2
- :released: April 2, 2019
-
- .. change::
- :tags: bug, documentation, sql
- :tickets: 4580
-
- Thanks to :ref:`change_3981`, we no longer need to rely on recipes that
- subclass dialect-specific types directly, :class:`.TypeDecorator` can now
- handle all cases. Additionally, the above change made it slightly less
- likely that a direct subclass of a base SQLAlchemy type would work as
- expected, which could be misleading. Documentation has been updated to use
- :class:`.TypeDecorator` for these examples including the PostgreSQL
- "ArrayOfEnum" example datatype and direct support for the "subclass a type
- directly" has been removed.
-
- .. change::
- :tags: bug, postgresql
- :tickets: 4550
-
- Modified the :paramref:`.Select.with_for_update.of` parameter so that if a
- join or other composed selectable is passed, the individual :class:`_schema.Table`
- objects will be filtered from it, allowing one to pass a join() object to
- the parameter, as occurs normally when using joined table inheritance with
- the ORM. Pull request courtesy Raymond Lu.
-
-
- .. change::
- :tags: feature, postgresql
- :tickets: 4562
-
- Added support for parameter-less connection URLs for the psycopg2 dialect,
- meaning, the URL can be passed to :func:`_sa.create_engine` as
- ``"postgresql+psycopg2://"`` with no additional arguments to indicate an
- empty DSN passed to libpq, which indicates to connect to "localhost" with
- no username, password, or database given. Pull request courtesy Julian
- Mehnle.
-
- .. change::
- :tags: bug, orm, ext
- :tickets: 4574, 4573
-
- Restored instance-level support for plain Python descriptors, e.g.
- ``@property`` objects, in conjunction with association proxies, in that if
- the proxied object is not within ORM scope at all, it gets classified as
- "ambiguous" but is proxed directly. For class level access, a basic class
- level``__get__()`` now returns the
- :class:`.AmbiguousAssociationProxyInstance` directly, rather than raising
- its exception, which is the closest approximation to the previous behavior
- that returned the :class:`.AssociationProxy` itself that's possible. Also
- improved the stringification of these objects to be more descriptive of
- current state.
-
- .. change::
- :tags: bug, orm
- :tickets: 4537
-
- Fixed bug where use of :func:`.with_polymorphic` or other aliased construct
- would not properly adapt when the aliased target were used as the
- :meth:`_expression.Select.correlate_except` target of a subquery used inside of a
- :func:`.column_property`. This required a fix to the clause adaption
- mechanics to properly handle a selectable that shows up in the "correlate
- except" list, in a similar manner as which occurs for selectables that show
- up in the "correlate" list. This is ultimately a fairly fundamental bug
- that has lasted for a long time but it is hard to come across it.
-
-
- .. change::
- :tags: bug, orm
- :tickets: 4566
-
- Fixed regression where a new error message that was supposed to raise when
- attempting to link a relationship option to an AliasedClass without using
- :meth:`.PropComparator.of_type` would instead raise an ``AttributeError``.
- Note that in 1.3, it is no longer valid to create an option path from a
- plain mapper relationship to an :class:`.AliasedClass` without using
- :meth:`.PropComparator.of_type`.
-
-.. changelog::
- :version: 1.3.1
- :released: March 9, 2019
-
- .. change::
- :tags: bug, mssql
- :tickets: 4525
-
- Fixed regression in SQL Server reflection due to :ticket:`4393` where the
- removal of open-ended ``**kw`` from the :class:`.Float` datatype caused
- reflection of this type to fail due to a "scale" argument being passed.
-
- .. change::
- :tags: bug, orm, ext
- :tickets: 4522
-
- Fixed regression where an association proxy linked to a synonym would no
- longer work, both at instance level and at class level.
-
-.. changelog::
- :version: 1.3.0
- :released: March 4, 2019
-
- .. change::
- :tags: feature, schema
- :tickets: 4517
-
- Added new parameters :paramref:`_schema.Table.resolve_fks` and
- :paramref:`.MetaData.reflect.resolve_fks` which when set to False will
- disable the automatic reflection of related tables encountered in
- :class:`_schema.ForeignKey` objects, which can both reduce SQL overhead for omitted
- tables as well as avoid tables that can't be reflected for database-specific
- reasons. Two :class:`_schema.Table` objects present in the same :class:`_schema.MetaData`
- collection can still refer to each other even if the reflection of the two
- tables occurred separately.
-
-
- .. change::
- :tags: feature, orm
- :tickets: 4316
-
- The :meth:`_query.Query.get` method can now accept a dictionary of attribute keys
- and values as a means of indicating the primary key value to load; is
- particularly useful for composite primary keys. Pull request courtesy
- Sanjana S.
-
- .. change::
- :tags: feature, orm
- :tickets: 3133
-
- A SQL expression can now be assigned to a primary key attribute for an ORM
- flush in the same manner as ordinary attributes as described in
- :ref:`flush_embedded_sql_expressions` where the expression will be evaluated
- and then returned to the ORM using RETURNING, or in the case of pysqlite,
- works using the cursor.lastrowid attribute.Requires either a database that
- supports RETURNING (e.g. Postgresql, Oracle, SQL Server) or pysqlite.
-
- .. change::
- :tags: bug, sql
- :tickets: 4509
-
- The :class:`_expression.Alias` class and related subclasses :class:`_expression.CTE`,
- :class:`_expression.Lateral` and :class:`_expression.TableSample` have been reworked so that it is
- not possible for a user to construct the objects directly. These constructs
- require that the standalone construction function or selectable-bound method
- be used to instantiate new objects.
-
-
- .. change::
- :tags: feature, engine
- :tickets: 4500
-
- Revised the formatting for :class:`.StatementError` when stringified. Each
- error detail is broken up over multiple newlines instead of spaced out on a
- single line. Additionally, the SQL representation now stringifies the SQL
- statement rather than using ``repr()``, so that newlines are rendered as is.
- Pull request courtesy Nate Clark.
-
- .. seealso::
-
- :ref:`change_4500`
-
-.. changelog::
- :version: 1.3.0b3
- :released: March 4, 2019
- :released: February 8, 2019
-
- .. change::
- :tags: bug, ext
- :tickets: 2642
-
- Implemented a more comprehensive assignment operation (e.g. "bulk replace")
- when using association proxy with sets or dictionaries. Fixes the problem
- of redundant proxy objects being created to replace the old ones, which
- leads to excessive events and SQL and in the case of unique constraints
- will cause the flush to fail.
-
- .. seealso::
-
- :ref:`change_2642`
-
- .. change::
- :tags: bug, postgresql
- :tickets: 4473
-
- Fixed issue where using an uppercase name for an index type (e.g. GIST,
- BTREE, etc. ) or an EXCLUDE constraint would treat it as an identifier to
- be quoted, rather than rendering it as is. The new behavior converts these
- types to lowercase and ensures they contain only valid SQL characters.
-
- .. change::
- :tags: bug, orm
- :tickets: 4469
-
- Improved the behavior of :func:`_orm.with_polymorphic` in conjunction with
- loader options, in particular wildcard operations as well as
- :func:`_orm.load_only`. The polymorphic object will be more accurately
- targeted so that column-level options on the entity will correctly take
- effect.The issue is a continuation of the same kinds of things fixed in
- :ticket:`4468`.
-
-
- .. change::
- :tags: bug, sql
- :tickets: 4481
-
- Fully removed the behavior of strings passed directly as components of a
- :func:`_expression.select` or :class:`_query.Query` object being coerced to :func:`_expression.text`
- constructs automatically; the warning that has been emitted is now an
- ArgumentError or in the case of order_by() / group_by() a CompileError.
- This has emitted a warning since version 1.0 however its presence continues
- to create concerns for the potential of mis-use of this behavior.
-
- Note that public CVEs have been posted for order_by() / group_by() which
- are resolved by this commit: CVE-2019-7164 CVE-2019-7548
-
-
- .. seealso::
-
- :ref:`change_4481`
-
- .. change::
- :tags: bug, sql
- :tickets: 4467
-
- Quoting is applied to :class:`.Function` names, those which are usually but
- not necessarily generated from the :attr:`_expression.func` construct, at compile
- time if they contain illegal characters, such as spaces or punctuation. The
- names are as before treated as case insensitive however, meaning if the
- names contain uppercase or mixed case characters, that alone does not
- trigger quoting. The case insensitivity is currently maintained for
- backwards compatibility.
-
-
- .. change::
- :tags: bug, sql
- :tickets: 4481
-
- Added "SQL phrase validation" to key DDL phrases that are accepted as plain
- strings, including :paramref:`_schema.ForeignKeyConstraint.on_delete`,
- :paramref:`_schema.ForeignKeyConstraint.on_update`,
- :paramref:`.ExcludeConstraint.using`,
- :paramref:`_schema.ForeignKeyConstraint.initially`, for areas where a series of SQL
- keywords only are expected.Any non-space characters that suggest the phrase
- would need to be quoted will raise a :class:`.CompileError`. This change
- is related to the series of changes committed as part of :ticket:`4481`.
-
- .. change::
- :tags: bug, orm, declarative
- :tickets: 4470
-
- Added some helper exceptions that invoke when a mapping based on
- :class:`.AbstractConcreteBase`, :class:`.DeferredReflection`, or
- :class:`.AutoMap` is used before the mapping is ready to be used, which
- contain descriptive information on the class, rather than falling through
- into other failure modes that are less informative.
-
-
- .. change::
- :tags: change, tests
- :tickets: 4460
-
- The test system has removed support for Nose, which is unmaintained for
- several years and is producing warnings under Python 3. The test suite is
- currently standardized on Pytest. Pull request courtesy Parth Shandilya.
-
-.. changelog::
- :version: 1.3.0b2
- :released: March 4, 2019
- :released: January 25, 2019
-
- .. change::
- :tags: bug, ext
- :tickets: 4401
-
- Fixed a regression in 1.3.0b1 caused by :ticket:`3423` where association
- proxy objects that access an attribute that's only present on a polymorphic
- subclass would raise an ``AttributeError`` even though the actual instance
- being accessed was an instance of that subclass.
-
- .. change::
- :tags: bug, orm
- :tickets: 1103
-
- Fixed long-standing issue where duplicate collection members would cause a
- backref to delete the association between the member and its parent object
- when one of the duplicates were removed, as occurs as a side effect of
- swapping two objects in one statement.
-
- .. seealso::
-
- :ref:`change_1103`
-
- .. change::
- :tags: bug, mssql
- :tickets: 4442
-
- The ``literal_processor`` for the :class:`.Unicode` and
- :class:`.UnicodeText` datatypes now render an ``N`` character in front of
- the literal string expression as required by SQL Server for Unicode string
- values rendered in SQL expressions.
-
- .. change::
- :tags: feature, orm
- :tickets: 4423
-
- Implemented a new feature whereby the :class:`.AliasedClass` construct can
- now be used as the target of a :func:`_orm.relationship`. This allows the
- concept of "non primary mappers" to no longer be necessary, as the
- :class:`.AliasedClass` is much easier to configure and automatically inherits
- all the relationships of the mapped class, as well as preserves the
- ability for loader options to work normally.
-
- .. seealso::
-
- :ref:`change_4423`
-
- .. change::
- :tags: bug, orm
- :tickets: 4373
-
- Extended the fix first made as part of :ticket:`3287`, where a loader option
- made against a subclass using a wildcard would extend itself to include
- application of the wildcard to attributes on the super classes as well, to a
- "bound" loader option as well, e.g. in an expression like
- ``Load(SomeSubClass).load_only('foo')``. Columns that are part of the
- parent class of ``SomeSubClass`` will also be excluded in the same way as if
- the unbound option ``load_only('foo')`` were used.
-
- .. change::
- :tags: bug, orm
- :tickets: 4433
-
- Improved error messages emitted by the ORM in the area of loader option
- traversal. This includes early detection of mis-matched loader strategies
- along with a clearer explanation why these strategies don't match.
-
-
- .. change::
- :tags: change, orm
- :tickets: 4412
-
- Added a new function :func:`.close_all_sessions` which takes
- over the task of the :meth:`.Session.close_all` method, which
- is now deprecated as this is confusing as a classmethod.
- Pull request courtesy Augustin Trancart.
-
- .. change::
- :tags: feature, orm
- :tickets: 4397
-
- Added new :meth:`.MapperEvents.before_mapper_configured` event. This
- event complements the other "configure" stage mapper events with a per
- mapper event that receives each :class:`_orm.Mapper` right before its
- configure step, and additionally may be used to prevent or delay the
- configuration of specific :class:`_orm.Mapper` objects using a new
- return value :attr:`.orm.interfaces.EXT_SKIP`. See the
- documentation link for an example.
-
- .. seealso::
-
- :meth:`.MapperEvents.before_mapper_configured`
-
-
-
- .. change::
- :tags: bug, orm
-
- The "remove" event for collections is now called before the item is removed
- in the case of the ``collection.remove()`` method, as is consistent with the
- behavior for most other forms of collection item removal (such as
- ``__delitem__``, replacement under ``__setitem__``). For ``pop()`` methods,
- the remove event still fires after the operation.
-
- .. change::
- :tags: bug, orm declarative
- :tickets: 4372
-
- Added a ``__clause_element__()`` method to :class:`.ColumnProperty` which
- can allow the usage of a not-fully-declared column or deferred attribute in
- a declarative mapped class slightly more friendly when it's used in a
- constraint or other column-oriented scenario within the class declaration,
- though this still can't work in open-ended expressions; prefer to call the
- :attr:`.ColumnProperty.expression` attribute if receiving ``TypeError``.
-
- .. change::
- :tags: bug, orm, engine
- :tickets: 4464
-
- Added accessors for execution options to Core and ORM, via
- :meth:`_query.Query.get_execution_options`,
- :meth:`_engine.Connection.get_execution_options`,
- :meth:`_engine.Engine.get_execution_options`, and
- :meth:`.Executable.get_execution_options`. PR courtesy Daniel Lister.
-
- .. change::
- :tags: bug, orm
- :tickets: 4446
-
- Fixed issue in association proxy due to :ticket:`3423` which caused the use
- of custom :class:`.PropComparator` objects with hybrid attributes, such as
- the one demonstrated in the ``dictlike-polymorphic`` example to not
- function within an association proxy. The strictness that was added in
- :ticket:`3423` has been relaxed, and additional logic to accommodate for
- an association proxy that links to a custom hybrid have been added.
-
- .. change::
- :tags: change, general
- :tickets: 4393
-
- A large change throughout the library has ensured that all objects,
- parameters, and behaviors which have been noted as deprecated or legacy now
- emit ``DeprecationWarning`` warnings when invoked.As the Python 3
- interpreter now defaults to displaying deprecation warnings, as well as that
- modern test suites based on tools like tox and pytest tend to display
- deprecation warnings, this change should make it easier to note what API
- features are obsolete. A major rationale for this change is so that long-
- deprecated features that nonetheless still see continue to see real world
- use can finally be removed in the near future; the biggest example of this
- are the :class:`.SessionExtension` and :class:`.MapperExtension` classes as
- well as a handful of other pre-event extension hooks, which have been
- deprecated since version 0.7 but still remain in the library. Another is
- that several major longstanding behaviors are to be deprecated as well,
- including the threadlocal engine strategy, the convert_unicode flag, and non
- primary mappers.
-
- .. seealso::
-
- :ref:`change_4393_general`
-
-
- .. change::
- :tags: change, engine
- :tickets: 4393
-
- The "threadlocal" engine strategy which has been a legacy feature of
- SQLAlchemy since around version 0.2 is now deprecated, along with the
- :paramref:`_pool.Pool.threadlocal` parameter of :class:`_pool.Pool` which has no
- effect in most modern use cases.
-
- .. seealso::
-
- :ref:`change_4393_threadlocal`
-
- .. change::
- :tags: change, sql
- :tickets: 4393
-
- The :paramref:`_sa.create_engine.convert_unicode` and
- :paramref:`.String.convert_unicode` parameters have been deprecated. These
- parameters were built back when most Python DBAPIs had little to no support
- for Python Unicode objects, and SQLAlchemy needed to take on the very
- complex task of marshalling data and SQL strings between Unicode and
- bytestrings throughout the system in a performant way. Thanks to Python 3,
- DBAPIs were compelled to adapt to Unicode-aware APIs and today all DBAPIs
- supported by SQLAlchemy support Unicode natively, including on Python 2,
- allowing this long-lived and very complicated feature to finally be (mostly)
- removed. There are still of course a few Python 2 edge cases where
- SQLAlchemy has to deal with Unicode however these are handled automatically;
- in modern use, there should be no need for end-user interaction with these
- flags.
-
- .. seealso::
-
- :ref:`change_4393_convertunicode`
-
- .. change::
- :tags: bug, orm
- :tickets: 3777
-
- Implemented the ``.get_history()`` method, which also implies availability
- of :attr:`.AttributeState.history`, for :func:`.synonym` attributes.
- Previously, trying to access attribute history via a synonym would raise an
- ``AttributeError``.
-
- .. change::
- :tags: feature, engine
- :tickets: 3689
-
- Added public accessor :meth:`.QueuePool.timeout` that returns the configured
- timeout for a :class:`.QueuePool` object. Pull request courtesy Irina Delamare.
-
- .. change::
- :tags: feature, sql
- :tickets: 4386
-
- Amended the :class:`.AnsiFunction` class, the base of common SQL
- functions like ``CURRENT_TIMESTAMP``, to accept positional arguments
- like a regular ad-hoc function. This to suit the case that many of
- these functions on specific backends accept arguments such as
- "fractional seconds" precision and such. If the function is created
- with arguments, it renders the parenthesis and the arguments. If
- no arguments are present, the compiler generates the non-parenthesized form.
-
-.. changelog::
- :version: 1.3.0b1
- :released: March 4, 2019
- :released: November 16, 2018
-
- .. change::
- :tags: bug, ext
- :tickets: 3423
-
- Reworked :class:`.AssociationProxy` to store state that's specific to a
- parent class in a separate object, so that a single
- :class:`.AssociationProxy` can serve for multiple parent classes, as is
- intrinsic to inheritance, without any ambiguity in the state returned by it.
- A new method :meth:`.AssociationProxy.for_class` is added to allow
- inspection of class-specific state.
-
- .. seealso::
-
- :ref:`change_3423`
-
-
- .. change::
- :tags: bug, oracle
- :tickets: 4369
-
- Updated the parameters that can be sent to the cx_Oracle DBAPI to both allow
- for all current parameters as well as for future parameters not added yet.
- In addition, removed unused parameters that were deprecated in version 1.2,
- and additionally we are now defaulting "threaded" to False.
-
- .. seealso::
-
- :ref:`change_4369`
-
- .. change::
- :tags: bug, oracle
- :tickets: 4242
-
- The Oracle dialect will no longer use the NCHAR/NCLOB datatypes
- represent generic unicode strings or clob fields in conjunction with
- :class:`.Unicode` and :class:`.UnicodeText` unless the flag
- ``use_nchar_for_unicode=True`` is passed to :func:`_sa.create_engine` -
- this includes CREATE TABLE behavior as well as ``setinputsizes()`` for
- bound parameters. On the read side, automatic Unicode conversion under
- Python 2 has been added to CHAR/VARCHAR/CLOB result rows, to match the
- behavior of cx_Oracle under Python 3. In order to mitigate the performance
- hit under Python 2, SQLAlchemy's very performant (when C extensions
- are built) native Unicode handlers are used under Python 2.
-
- .. seealso::
-
- :ref:`change_4242`
-
- .. change::
- :tags: bug, orm
- :tickets: 3844
-
- Fixed issue regarding passive_deletes="all", where the foreign key
- attribute of an object is maintained with its value even after the object
- is removed from its parent collection. Previously, the unit of work would
- set this to NULL even though passive_deletes indicated it should not be
- modified.
-
- .. seealso::
-
- :ref:`change_3844`
-
- .. change::
- :tags: bug, ext
- :tickets: 4268
-
- The long-standing behavior of the association proxy collection maintaining
- only a weak reference to the parent object is reverted; the proxy will now
- maintain a strong reference to the parent for as long as the proxy
- collection itself is also in memory, eliminating the "stale association
- proxy" error. This change is being made on an experimental basis to see if
- any use cases arise where it causes side effects.
-
- .. seealso::
-
- :ref:`change_4268`
-
-
- .. change::
- :tags: bug, sql
- :tickets: 4302
-
- Added "like" based operators as "comparison" operators, including
- :meth:`.ColumnOperators.startswith` :meth:`.ColumnOperators.endswith`
- :meth:`.ColumnOperators.ilike` :meth:`.ColumnOperators.notilike` among many
- others, so that all of these operators can be the basis for an ORM
- "primaryjoin" condition.
-
-
- .. change::
- :tags: feature, sqlite
- :tickets: 3850
-
- Added support for SQLite's json functionality via the new
- SQLite implementation for :class:`_types.JSON`, :class:`_sqlite.JSON`.
- The name used for the type is ``JSON``, following an example found at
- SQLite's own documentation. Pull request courtesy Ilja Everilä.
-
- .. seealso::
-
- :ref:`change_3850`
-
- .. change::
- :tags: feature, engine
-
- Added new "lifo" mode to :class:`.QueuePool`, typically enabled by setting
- the flag :paramref:`_sa.create_engine.pool_use_lifo` to True. "lifo" mode
- means the same connection just checked in will be the first to be checked
- out again, allowing excess connections to be cleaned up from the server
- side during periods of the pool being only partially utilized. Pull request
- courtesy Taem Park.
-
- .. seealso::
-
- :ref:`change_pr467`
-
- .. change::
- :tags: bug, orm
- :tickets: 4359
-
- Improved the behavior of a relationship-bound many-to-one object expression
- such that the retrieval of column values on the related object are now
- resilient against the object being detached from its parent
- :class:`.Session`, even if the attribute has been expired. New features
- within the :class:`.InstanceState` are used to memoize the last known value
- of a particular column attribute before its expired, so that the expression
- can still evaluate when the object is detached and expired at the same
- time. Error conditions are also improved using modern attribute state
- features to produce more specific messages as needed.
-
- .. seealso::
-
- :ref:`change_4359`
-
- .. change::
- :tags: feature, mysql
- :tickets: 4219
-
- Support added for the "WITH PARSER" syntax of CREATE FULLTEXT INDEX
- in MySQL, using the ``mysql_with_parser`` keyword argument. Reflection
- is also supported, which accommodates MySQL's special comment format
- for reporting on this option as well. Additionally, the "FULLTEXT" and
- "SPATIAL" index prefixes are now reflected back into the ``mysql_prefix``
- index option.
-
-
-
- .. change::
- :tags: bug, orm, mysql, postgresql
- :tickets: 4246
-
- The ORM now doubles the "FOR UPDATE" clause within the subquery that
- renders in conjunction with joined eager loading in some cases, as it has
- been observed that MySQL does not lock the rows from a subquery. This
- means the query renders with two FOR UPDATE clauses; note that on some
- backends such as Oracle, FOR UPDATE clauses on subqueries are silently
- ignored since they are unnecessary. Additionally, in the case of the "OF"
- clause used primarily with PostgreSQL, the FOR UPDATE is rendered only on
- the inner subquery when this is used so that the selectable can be targeted
- to the table within the SELECT statement.
-
- .. seealso::
-
- :ref:`change_4246`
-
- .. change::
- :tags: feature, mssql
- :tickets: 4158
-
- Added ``fast_executemany=True`` parameter to the SQL Server pyodbc dialect,
- which enables use of pyodbc's new performance feature of the same name
- when using Microsoft ODBC drivers.
-
- .. seealso::
-
- :ref:`change_4158`
-
- .. change::
- :tags: bug, ext
- :tickets: 4308
-
- Fixed multiple issues regarding de-association of scalar objects with the
- association proxy. ``del`` now works, and additionally a new flag
- :paramref:`.AssociationProxy.cascade_scalar_deletes` is added, which when
- set to True indicates that setting a scalar attribute to ``None`` or
- deleting via ``del`` will also set the source association to ``None``.
-
- .. seealso::
-
- :ref:`change_4308`
-
-
- .. change::
- :tags: feature, ext
- :tickets: 4318
-
- Added new feature :meth:`.BakedQuery.to_query`, which allows for a
- clean way of using one :class:`.BakedQuery` as a subquery inside of another
- :class:`.BakedQuery` without needing to refer explicitly to a
- :class:`.Session`.
-
-
- .. change::
- :tags: feature, sqlite
- :tickets: 4360
-
- Implemented the SQLite ``ON CONFLICT`` clause as understood at the DDL
- level, e.g. for primary key, unique, and CHECK constraints as well as
- specified on a :class:`_schema.Column` to satisfy inline primary key and NOT NULL.
- Pull request courtesy Denis Kataev.
-
- .. seealso::
-
- :ref:`change_4360`
-
- .. change::
- :tags: feature, postgresql
- :tickets: 4237
-
- Added rudimental support for reflection of PostgreSQL
- partitioned tables, e.g. that relkind='p' is added to reflection
- queries that return table information.
-
- .. seealso::
-
- :ref:`change_4237`
-
- .. change::
- :tags: feature, ext
- :tickets: 4351
-
- The :class:`.AssociationProxy` now has standard column comparison operations
- such as :meth:`.ColumnOperators.like` and
- :meth:`.ColumnOperators.startswith` available when the target attribute is a
- plain column - the EXISTS expression that joins to the target table is
- rendered as usual, but the column expression is then use within the WHERE
- criteria of the EXISTS. Note that this alters the behavior of the
- ``.contains()`` method on the association proxy to make use of
- :meth:`.ColumnOperators.contains` when used on a column-based attribute.
-
- .. seealso::
-
- :ref:`change_4351`
-
-
- .. change::
- :tags: feature, orm
-
- Added new flag :paramref:`.Session.bulk_save_objects.preserve_order` to the
- :meth:`.Session.bulk_save_objects` method, which defaults to True. When set
- to False, the given mappings will be grouped into inserts and updates per
- each object type, to allow for greater opportunities to batch common
- operations together. Pull request courtesy Alessandro Cucci.
-
- .. change::
- :tags: bug, orm
- :tickets: 4365
-
- Refactored :meth:`_query.Query.join` to further clarify the individual components
- of structuring the join. This refactor adds the ability for
- :meth:`_query.Query.join` to determine the most appropriate "left" side of the
- join when there is more than one element in the FROM list or the query is
- against multiple entities. If more than one FROM/entity matches, an error
- is raised that asks for an ON clause to be specified to resolve the
- ambiguity. In particular this targets the regression we saw in
- :ticket:`4363` but is also of general use. The codepaths within
- :meth:`_query.Query.join` are now easier to follow and the error cases are
- decided more specifically at an earlier point in the operation.
-
- .. seealso::
-
- :ref:`change_4365`
-
- .. change::
- :tags: bug, sql
- :tickets: 3981
-
- Fixed issue with :meth:`.TypeEngine.bind_expression` and
- :meth:`.TypeEngine.column_expression` methods where these methods would not
- work if the target type were part of a :class:`.Variant`, or other target
- type of a :class:`.TypeDecorator`. Additionally, the SQL compiler now
- calls upon the dialect-level implementation when it renders these methods
- so that dialects can now provide for SQL-level processing for built-in
- types.
-
- .. seealso::
-
- :ref:`change_3981`
-
-
- .. change::
- :tags: bug, orm
- :tickets: 4304
-
- Fixed long-standing issue in :class:`_query.Query` where a scalar subquery such
- as produced by :meth:`_query.Query.exists`, :meth:`_query.Query.as_scalar` and other
- derivations from :attr:`_query.Query.statement` would not correctly be adapted
- when used in a new :class:`_query.Query` that required entity adaptation, such as
- when the query were turned into a union, or a from_self(), etc. The change
- removes the "no adaptation" annotation from the :func:`_expression.select` object
- produced by the :attr:`_query.Query.statement` accessor.
-
- .. change::
- :tags: bug, orm, declarative
- :tickets: 4133
-
- Fixed bug where declarative would not update the state of the
- :class:`_orm.Mapper` as far as what attributes were present, when additional
- attributes were added or removed after the mapper attribute collections had
- already been called and memoized. Additionally, a ``NotImplementedError``
- is now raised if a fully mapped attribute (e.g. column, relationship, etc.)
- is deleted from a class that is currently mapped, since the mapper will not
- function correctly if the attribute has been removed.
-
- .. change::
- :tags: bug, mssql
- :tickets: 4362
-
- Deprecated the use of :class:`.Sequence` with SQL Server in order to affect
- the "start" and "increment" of the IDENTITY value, in favor of new
- parameters ``mssql_identity_start`` and ``mssql_identity_increment`` which
- set these parameters directly. :class:`.Sequence` will be used to generate
- real ``CREATE SEQUENCE`` DDL with SQL Server in a future release.
-
- .. seealso::
-
- :ref:`change_4362`
-
-
- .. change::
- :tags: feature, mysql
-
- Added support for the parameters in an ON DUPLICATE KEY UPDATE statement on
- MySQL to be ordered, since parameter order in a MySQL UPDATE clause is
- significant, in a similar manner as that described at
- :ref:`tutorial_parameter_ordered_updates`. Pull request courtesy Maxim Bublis.
-
- .. seealso::
-
- :ref:`change_mysql_ondupordering`
-
- .. change::
- :tags: feature, sql
- :tickets: 4144
-
- Added :class:`.Sequence` to the "string SQL" system that will render a
- meaningful string expression (``"<next sequence value: my_sequence>"``)
- when stringifying without a dialect a statement that includes a "sequence
- nextvalue" expression, rather than raising a compilation error.
-
-
-
- .. change::
- :tags: bug, orm
- :tickets: 4232
-
- An informative exception is re-raised when a primary key value is not
- sortable in Python during an ORM flush under Python 3, such as an ``Enum``
- that has no ``__lt__()`` method; normally Python 3 raises a ``TypeError``
- in this case. The flush process sorts persistent objects by primary key
- in Python so the values must be sortable.
-
-
- .. change::
- :tags: orm, bug
- :tickets: 3604
-
- Removed the collection converter used by the :class:`.MappedCollection`
- class. This converter was used only to assert that the incoming dictionary
- keys matched that of their corresponding objects, and only during a bulk set
- operation. The converter can interfere with a custom validator or
- :meth:`.AttributeEvents.bulk_replace` listener that wants to convert
- incoming values further. The ``TypeError`` which would be raised by this
- converter when an incoming key didn't match the value is removed; incoming
- values during a bulk assignment will be keyed to their value-generated key,
- and not the key that's explicitly present in the dictionary.
-
- Overall, @converter is superseded by the
- :meth:`.AttributeEvents.bulk_replace` event handler added as part of
- :ticket:`3896`.
-
- .. change::
- :tags: feature, sql
- :tickets: 3989
-
- Added new naming convention tokens ``column_0N_name``, ``column_0_N_name``,
- etc., which will render the names / keys / labels for all columns referenced
- by a particular constraint in a sequence. In order to accommodate for the
- length of such a naming convention, the SQL compiler's auto-truncation
- feature now applies itself to constraint names as well, which creates a
- shortened, deterministically generated name for the constraint that will
- apply to a target backend without going over the character limit of that
- backend.
-
- The change also repairs two other issues. One is that the ``column_0_key``
- token wasn't available even though this token was documented, the other was
- that the ``referred_column_0_name`` token would inadvertently render the
- ``.key`` and not the ``.name`` of the column if these two values were
- different.
-
- .. seealso::
-
- :ref:`change_3989`
-
-
- .. change::
- :tags: feature, ext
- :tickets: 4196
-
- Added support for bulk :meth:`_query.Query.update` and :meth:`_query.Query.delete`
- to the :class:`.ShardedQuery` class within the horizontal sharding
- extension. This also adds an additional expansion hook to the
- bulk update/delete methods :meth:`_query.Query._execute_crud`.
-
- .. seealso::
-
- :ref:`change_4196`
-
- .. change::
- :tags: feature, sql
- :tickets: 4271
-
- Added new logic to the "expanding IN" bound parameter feature whereby if
- the given list is empty, a special "empty set" expression that is specific
- to different backends is generated, thus allowing IN expressions to be
- fully dynamic including empty IN expressions.
-
- .. seealso::
-
- :ref:`change_4271`
-
-
-
- .. change::
- :tags: feature, mysql
-
- The "pre-ping" feature of the connection pool now uses
- the ``ping()`` method of the DBAPI connection in the case of
- mysqlclient, PyMySQL and mysql-connector-python. Pull request
- courtesy Maxim Bublis.
-
- .. seealso::
-
- :ref:`change_mysql_ping`
-
- .. change::
- :tags: feature, orm
- :tickets: 4340
-
- The "selectin" loader strategy now omits the JOIN in the case of a simple
- one-to-many load, where it instead relies loads only from the related
- table, relying upon the foreign key columns of the related table in order
- to match up to primary keys in the parent table. This optimization can be
- disabled by setting the :paramref:`_orm.relationship.omit_join` flag to False.
- Many thanks to Jayson Reis for the efforts on this.
-
- .. seealso::
-
- :ref:`change_4340`
-
- .. change::
- :tags: bug, orm
- :tickets: 4353
-
- Added new behavior to the lazy load that takes place when the "old" value of
- a many-to-one is retrieved, such that exceptions which would be raised due
- to either ``lazy="raise"`` or a detached session error are skipped.
-
- .. seealso::
-
- :ref:`change_4353`
-
- .. change::
- :tags: feature, sql
-
- The Python builtin ``dir()`` is now supported for a SQLAlchemy "properties"
- object, such as that of a Core columns collection (e.g. ``.c``),
- ``mapper.attrs``, etc. Allows iPython autocompletion to work as well.
- Pull request courtesy Uwe Korn.
-
- .. change::
- :tags: feature, orm
- :tickets: 4257
-
- Added ``.info`` dictionary to the :class:`.InstanceState` class, the object
- that comes from calling :func:`_sa.inspect` on a mapped object.
-
- .. seealso::
-
- :ref:`change_4257`
-
- .. change::
- :tags: feature, sql
- :tickets: 3831
-
- Added new feature :meth:`.FunctionElement.as_comparison` which allows a SQL
- function to act as a binary comparison operation that can work within the
- ORM.
-
- .. seealso::
-
- :ref:`change_3831`
-
- .. change::
- :tags: bug, orm
- :tickets: 4354
-
- A long-standing oversight in the ORM, the ``__delete__`` method for a many-
- to-one relationship was non-functional, e.g. for an operation such as ``del
- a.b``. This is now implemented and is equivalent to setting the attribute
- to ``None``.
-
- .. seealso::
-
- :ref:`change_4354`
changelog_20
changelog_14
- changelog_13
- changelog_12
- changelog_11
- changelog_10
- changelog_09
- changelog_08
- changelog_07
- changelog_06
- changelog_05
- changelog_04
- changelog_03
- changelog_02
- changelog_01
Older Migration Guides
migration_14
migration_13
- migration_12
- migration_11
- migration_10
- migration_09
- migration_08
- migration_07
- migration_06
- migration_05
- migration_04
+++ /dev/null
-=============================
-What's new in SQLAlchemy 0.4?
-=============================
-
-.. admonition:: About this Document
-
- This document describes changes between SQLAlchemy version 0.3,
- last released October 14, 2007, and SQLAlchemy version 0.4,
- last released October 12, 2008.
-
- Document date: March 21, 2008
-
-First Things First
-==================
-
-If you're using any ORM features, make sure you import from
-``sqlalchemy.orm``:
-
-::
-
- from sqlalchemy import *
- from sqlalchemy.orm import *
-
-Secondly, anywhere you used to say ``engine=``,
-``connectable=``, ``bind_to=``, ``something.engine``,
-``metadata.connect()``, use ``bind``:
-
-::
-
- myengine = create_engine("sqlite://")
-
- meta = MetaData(myengine)
-
- meta2 = MetaData()
- meta2.bind = myengine
-
- session = create_session(bind=myengine)
-
- statement = select([table], bind=myengine)
-
-Got those ? Good! You're now (95%) 0.4 compatible. If
-you're using 0.3.10, you can make these changes immediately;
-they'll work there too.
-
-Module Imports
-==============
-
-In 0.3, "``from sqlalchemy import *``" would import all of
-sqlalchemy's sub-modules into your namespace. Version 0.4 no
-longer imports sub-modules into the namespace. This may mean
-you need to add extra imports into your code.
-
-In 0.3, this code worked:
-
-::
-
- from sqlalchemy import *
-
-
- class UTCDateTime(types.TypeDecorator):
- pass
-
-In 0.4, one must do:
-
-::
-
- from sqlalchemy import *
- from sqlalchemy import types
-
-
- class UTCDateTime(types.TypeDecorator):
- pass
-
-Object Relational Mapping
-=========================
-
-Querying
---------
-
-New Query API
-^^^^^^^^^^^^^
-
-Query is standardized on the generative interface (old
-interface is still there, just deprecated). While most of
-the generative interface is available in 0.3, the 0.4 Query
-has the inner guts to match the generative outside, and has
-a lot more tricks. All result narrowing is via ``filter()``
-and ``filter_by()``, limiting/offset is either through array
-slices or ``limit()``/``offset()``, joining is via
-``join()`` and ``outerjoin()`` (or more manually, through
-``select_from()`` as well as manually-formed criteria).
-
-To avoid deprecation warnings, you must make some changes to
-your 03 code
-
-User.query.get_by( \**kwargs )
-
-::
-
- User.query.filter_by(**kwargs).first()
-
-User.query.select_by( \**kwargs )
-
-::
-
- User.query.filter_by(**kwargs).all()
-
-User.query.select()
-
-::
-
- User.query.filter(xxx).all()
-
-New Property-Based Expression Constructs
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-By far the most palpable difference within the ORM is that
-you can now construct your query criterion using class-based
-attributes directly. The ".c." prefix is no longer needed
-when working with mapped classes:
-
-::
-
- session.query(User).filter(and_(User.name == "fred", User.id > 17))
-
-While simple column-based comparisons are no big deal, the
-class attributes have some new "higher level" constructs
-available, including what was previously only available in
-``filter_by()``:
-
-::
-
- # comparison of scalar relations to an instance
- filter(Address.user == user)
-
- # return all users who contain a particular address
- filter(User.addresses.contains(address))
-
- # return all users who *dont* contain the address
- filter(~User.address.contains(address))
-
- # return all users who contain a particular address with
- # the email_address like '%foo%'
- filter(User.addresses.any(Address.email_address.like("%foo%")))
-
- # same, email address equals 'foo@bar.com'. can fall back to keyword
- # args for simple comparisons
- filter(User.addresses.any(email_address="foo@bar.com"))
-
- # return all Addresses whose user attribute has the username 'ed'
- filter(Address.user.has(name="ed"))
-
- # return all Addresses whose user attribute has the username 'ed'
- # and an id > 5 (mixing clauses with kwargs)
- filter(Address.user.has(User.id > 5, name="ed"))
-
-The ``Column`` collection remains available on mapped
-classes in the ``.c`` attribute. Note that property-based
-expressions are only available with mapped properties of
-mapped classes. ``.c`` is still used to access columns in
-regular tables and selectable objects produced from SQL
-Expressions.
-
-Automatic Join Aliasing
-^^^^^^^^^^^^^^^^^^^^^^^
-
-We've had join() and outerjoin() for a while now:
-
-::
-
- session.query(Order).join("items")
-
-Now you can alias them:
-
-::
-
- session.query(Order).join("items", aliased=True).filter(Item.name="item 1").join(
- "items", aliased=True
- ).filter(Item.name == "item 3")
-
-The above will create two joins from orders->items using
-aliases. the ``filter()`` call subsequent to each will
-adjust its table criterion to that of the alias. To get at
-the ``Item`` objects, use ``add_entity()`` and target each
-join with an ``id``:
-
-::
-
- session.query(Order).join("items", id="j1", aliased=True).filter(
- Item.name == "item 1"
- ).join("items", aliased=True, id="j2").filter(Item.name == "item 3").add_entity(
- Item, id="j1"
- ).add_entity(
- Item, id="j2"
- )
-
-Returns tuples in the form: ``(Order, Item, Item)``.
-
-Self-referential Queries
-^^^^^^^^^^^^^^^^^^^^^^^^
-
-So query.join() can make aliases now. What does that give
-us ? Self-referential queries ! Joins can be done without
-any ``Alias`` objects:
-
-::
-
- # standard self-referential TreeNode mapper with backref
- mapper(
- TreeNode,
- tree_nodes,
- properties={
- "children": relation(
- TreeNode, backref=backref("parent", remote_side=tree_nodes.id)
- )
- },
- )
-
- # query for node with child containing "bar" two levels deep
- session.query(TreeNode).join(["children", "children"], aliased=True).filter_by(
- name="bar"
- )
-
-To add criterion for each table along the way in an aliased
-join, you can use ``from_joinpoint`` to keep joining against
-the same line of aliases:
-
-::
-
- # search for the treenode along the path "n1/n12/n122"
-
- # first find a Node with name="n122"
- q = sess.query(Node).filter_by(name="n122")
-
- # then join to parent with "n12"
- q = q.join("parent", aliased=True).filter_by(name="n12")
-
- # join again to the next parent with 'n1'. use 'from_joinpoint'
- # so we join from the previous point, instead of joining off the
- # root table
- q = q.join("parent", aliased=True, from_joinpoint=True).filter_by(name="n1")
-
- node = q.first()
-
-``query.populate_existing()``
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The eager version of ``query.load()`` (or
-``session.refresh()``). Every instance loaded from the
-query, including all eagerly loaded items, get refreshed
-immediately if already present in the session:
-
-::
-
- session.query(Blah).populate_existing().all()
-
-Relations
----------
-
-SQL Clauses Embedded in Updates/Inserts
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-For inline execution of SQL clauses, embedded right in the
-UPDATE or INSERT, during a ``flush()``:
-
-::
-
-
- myobject.foo = mytable.c.value + 1
-
- user.pwhash = func.md5(password)
-
- order.hash = text("select hash from hashing_table")
-
-The column-attribute is set up with a deferred loader after
-the operation, so that it issues the SQL to load the new
-value when you next access.
-
-Self-referential and Cyclical Eager Loading
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Since our alias-fu has improved, ``relation()`` can join
-along the same table \*any number of times*; you tell it how
-deep you want to go. Lets show the self-referential
-``TreeNode`` more clearly:
-
-::
-
- nodes = Table(
- "nodes",
- metadata,
- Column("id", Integer, primary_key=True),
- Column("parent_id", Integer, ForeignKey("nodes.id")),
- Column("name", String(30)),
- )
-
-
- class TreeNode(object):
- pass
-
-
- mapper(
- TreeNode,
- nodes,
- properties={"children": relation(TreeNode, lazy=False, join_depth=3)},
- )
-
-So what happens when we say:
-
-::
-
- create_session().query(TreeNode).all()
-
-? A join along aliases, three levels deep off the parent:
-
-.. sourcecode:: sql
-
- SELECT
- nodes_3.id AS nodes_3_id, nodes_3.parent_id AS nodes_3_parent_id, nodes_3.name AS nodes_3_name,
- nodes_2.id AS nodes_2_id, nodes_2.parent_id AS nodes_2_parent_id, nodes_2.name AS nodes_2_name,
- nodes_1.id AS nodes_1_id, nodes_1.parent_id AS nodes_1_parent_id, nodes_1.name AS nodes_1_name,
- nodes.id AS nodes_id, nodes.parent_id AS nodes_parent_id, nodes.name AS nodes_name
- FROM nodes LEFT OUTER JOIN nodes AS nodes_1 ON nodes.id = nodes_1.parent_id
- LEFT OUTER JOIN nodes AS nodes_2 ON nodes_1.id = nodes_2.parent_id
- LEFT OUTER JOIN nodes AS nodes_3 ON nodes_2.id = nodes_3.parent_id
- ORDER BY nodes.oid, nodes_1.oid, nodes_2.oid, nodes_3.oid
-
-Notice the nice clean alias names too. The joining doesn't
-care if it's against the same immediate table or some other
-object which then cycles back to the beginning. Any kind
-of chain of eager loads can cycle back onto itself when
-``join_depth`` is specified. When not present, eager
-loading automatically stops when it hits a cycle.
-
-Composite Types
-^^^^^^^^^^^^^^^
-
-This is one from the Hibernate camp. Composite Types let
-you define a custom datatype that is composed of more than
-one column (or one column, if you wanted). Lets define a
-new type, ``Point``. Stores an x/y coordinate:
-
-::
-
- class Point(object):
- def __init__(self, x, y):
- self.x = x
- self.y = y
-
- def __composite_values__(self):
- return self.x, self.y
-
- def __eq__(self, other):
- return other.x == self.x and other.y == self.y
-
- def __ne__(self, other):
- return not self.__eq__(other)
-
-The way the ``Point`` object is defined is specific to a
-custom type; constructor takes a list of arguments, and the
-``__composite_values__()`` method produces a sequence of
-those arguments. The order will match up to our mapper, as
-we'll see in a moment.
-
-Let's create a table of vertices storing two points per row:
-
-::
-
- vertices = Table(
- "vertices",
- metadata,
- Column("id", Integer, primary_key=True),
- Column("x1", Integer),
- Column("y1", Integer),
- Column("x2", Integer),
- Column("y2", Integer),
- )
-
-Then, map it ! We'll create a ``Vertex`` object which
-stores two ``Point`` objects:
-
-::
-
- class Vertex(object):
- def __init__(self, start, end):
- self.start = start
- self.end = end
-
-
- mapper(
- Vertex,
- vertices,
- properties={
- "start": composite(Point, vertices.c.x1, vertices.c.y1),
- "end": composite(Point, vertices.c.x2, vertices.c.y2),
- },
- )
-
-Once you've set up your composite type, it's usable just
-like any other type:
-
-::
-
-
- v = Vertex(Point(3, 4), Point(26, 15))
- session.save(v)
- session.flush()
-
- # works in queries too
- q = session.query(Vertex).filter(Vertex.start == Point(3, 4))
-
-If you'd like to define the way the mapped attributes
-generate SQL clauses when used in expressions, create your
-own ``sqlalchemy.orm.PropComparator`` subclass, defining any
-of the common operators (like ``__eq__()``, ``__le__()``,
-etc.), and send it in to ``composite()``. Composite types
-work as primary keys too, and are usable in ``query.get()``:
-
-::
-
- # a Document class which uses a composite Version
- # object as primary key
- document = query.get(Version(1, "a"))
-
-``dynamic_loader()`` relations
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-A ``relation()`` that returns a live ``Query`` object for
-all read operations. Write operations are limited to just
-``append()`` and ``remove()``, changes to the collection are
-not visible until the session is flushed. This feature is
-particularly handy with an "autoflushing" session which will
-flush before each query.
-
-::
-
- mapper(
- Foo,
- foo_table,
- properties={
- "bars": dynamic_loader(
- Bar,
- backref="foo",
- # <other relation() opts>
- )
- },
- )
-
- session = create_session(autoflush=True)
- foo = session.query(Foo).first()
-
- foo.bars.append(Bar(name="lala"))
-
- for bar in foo.bars.filter(Bar.name == "lala"):
- print(bar)
-
- session.commit()
-
-New Options: ``undefer_group()``, ``eagerload_all()``
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-A couple of query options which are handy.
-``undefer_group()`` marks a whole group of "deferred"
-columns as undeferred:
-
-::
-
- mapper(
- Class,
- table,
- properties={
- "foo": deferred(table.c.foo, group="group1"),
- "bar": deferred(table.c.bar, group="group1"),
- "bat": deferred(table.c.bat, group="group1"),
- },
- )
-
- session.query(Class).options(undefer_group("group1")).filter(...).all()
-
-and ``eagerload_all()`` sets a chain of attributes to be
-eager in one pass:
-
-::
-
- mapper(Foo, foo_table, properties={"bar": relation(Bar)})
- mapper(Bar, bar_table, properties={"bat": relation(Bat)})
- mapper(Bat, bat_table)
-
- # eager load bar and bat
- session.query(Foo).options(eagerload_all("bar.bat")).filter(...).all()
-
-New Collection API
-^^^^^^^^^^^^^^^^^^
-
-Collections are no longer proxied by an
-{{{InstrumentedList}}} proxy, and access to members, methods
-and attributes is direct. Decorators now intercept objects
-entering and leaving the collection, and it is now possible
-to easily write a custom collection class that manages its
-own membership. Flexible decorators also replace the named
-method interface of custom collections in 0.3, allowing any
-class to be easily adapted to use as a collection container.
-
-Dictionary-based collections are now much easier to use and
-fully ``dict``-like. Changing ``__iter__`` is no longer
-needed for ``dict``s, and new built-in ``dict`` types cover
-many needs:
-
-::
-
- # use a dictionary relation keyed by a column
- relation(Item, collection_class=column_mapped_collection(items.c.keyword))
- # or named attribute
- relation(Item, collection_class=attribute_mapped_collection("keyword"))
- # or any function you like
- relation(Item, collection_class=mapped_collection(lambda entity: entity.a + entity.b))
-
-Existing 0.3 ``dict``-like and freeform object derived
-collection classes will need to be updated for the new API.
-In most cases this is simply a matter of adding a couple
-decorators to the class definition.
-
-Mapped Relations from External Tables/Subqueries
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-This feature quietly appeared in 0.3 but has been improved
-in 0.4 thanks to better ability to convert subqueries
-against a table into subqueries against an alias of that
-table; this is key for eager loading, aliased joins in
-queries, etc. It reduces the need to create mappers against
-select statements when you just need to add some extra
-columns or subqueries:
-
-::
-
- mapper(
- User,
- users,
- properties={
- "fullname": column_property(
- (users.c.firstname + users.c.lastname).label("fullname")
- ),
- "numposts": column_property(
- select([func.count(1)], users.c.id == posts.c.user_id)
- .correlate(users)
- .label("posts")
- ),
- },
- )
-
-a typical query looks like:
-
-.. sourcecode:: sql
-
- SELECT (SELECT count(1) FROM posts WHERE users.id = posts.user_id) AS count,
- users.firstname || users.lastname AS fullname,
- users.id AS users_id, users.firstname AS users_firstname, users.lastname AS users_lastname
- FROM users ORDER BY users.oid
-
-Horizontal Scaling (Sharding) API
----------------------------------
-
-[browser:/sqlalchemy/trunk/examples/sharding/attribute_shard
-.py]
-
-Sessions
---------
-
-New Session Create Paradigm; SessionContext, assignmapper Deprecated
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-That's right, the whole shebang is being replaced with two
-configurational functions. Using both will produce the most
-0.1-ish feel we've had since 0.1 (i.e., the least amount of
-typing).
-
-Configure your own ``Session`` class right where you define
-your ``engine`` (or anywhere):
-
-::
-
- from sqlalchemy import create_engine
- from sqlalchemy.orm import sessionmaker
-
- engine = create_engine("myengine://")
- Session = sessionmaker(bind=engine, autoflush=True, transactional=True)
-
- # use the new Session() freely
- sess = Session()
- sess.save(someobject)
- sess.flush()
-
-If you need to post-configure your Session, say with an
-engine, add it later with ``configure()``:
-
-::
-
- Session.configure(bind=create_engine(...))
-
-All the behaviors of ``SessionContext`` and the ``query``
-and ``__init__`` methods of ``assignmapper`` are moved into
-the new ``scoped_session()`` function, which is compatible
-with both ``sessionmaker`` as well as ``create_session()``:
-
-::
-
- from sqlalchemy.orm import scoped_session, sessionmaker
-
- Session = scoped_session(sessionmaker(autoflush=True, transactional=True))
- Session.configure(bind=engine)
-
- u = User(name="wendy")
-
- sess = Session()
- sess.save(u)
- sess.commit()
-
- # Session constructor is thread-locally scoped. Everyone gets the same
- # Session in the thread when scope="thread".
- sess2 = Session()
- assert sess is sess2
-
-When using a thread-local ``Session``, the returned class
-has all of ``Session's`` interface implemented as
-classmethods, and "assignmapper"'s functionality is
-available using the ``mapper`` classmethod. Just like the
-old ``objectstore`` days....
-
-::
-
-
- # "assignmapper"-like functionality available via ScopedSession.mapper
- Session.mapper(User, users_table)
-
- u = User(name="wendy")
-
- Session.commit()
-
-Sessions are again Weak Referencing By Default
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The weak_identity_map flag is now set to ``True`` by default
-on Session. Instances which are externally deferenced and
-fall out of scope are removed from the session
-automatically. However, items which have "dirty" changes
-present will remain strongly referenced until those changes
-are flushed at which case the object reverts to being weakly
-referenced (this works for 'mutable' types, like picklable
-attributes, as well). Setting weak_identity_map to
-``False`` restores the old strong-referencing behavior for
-those of you using the session like a cache.
-
-Auto-Transactional Sessions
-^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-As you might have noticed above, we are calling ``commit()``
-on ``Session``. The flag ``transactional=True`` means the
-``Session`` is always in a transaction, ``commit()``
-persists permanently.
-
-Auto-Flushing Sessions
-^^^^^^^^^^^^^^^^^^^^^^
-
-Also, ``autoflush=True`` means the ``Session`` will
-``flush()`` before each ``query`` as well as when you call
-``flush()`` or ``commit()``. So now this will work:
-
-::
-
- Session = sessionmaker(bind=engine, autoflush=True, transactional=True)
-
- u = User(name="wendy")
-
- sess = Session()
- sess.save(u)
-
- # wendy is flushed, comes right back from a query
- wendy = sess.query(User).filter_by(name="wendy").one()
-
-Transactional methods moved onto sessions
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-``commit()`` and ``rollback()``, as well as ``begin()`` are
-now directly on ``Session``. No more need to use
-``SessionTransaction`` for anything (it remains in the
-background).
-
-::
-
- Session = sessionmaker(autoflush=True, transactional=False)
-
- sess = Session()
- sess.begin()
-
- # use the session
-
- sess.commit() # commit transaction
-
-Sharing a ``Session`` with an enclosing engine-level (i.e.
-non-ORM) transaction is easy:
-
-::
-
- Session = sessionmaker(autoflush=True, transactional=False)
-
- conn = engine.connect()
- trans = conn.begin()
- sess = Session(bind=conn)
-
- # ... session is transactional
-
- # commit the outermost transaction
- trans.commit()
-
-Nested Session Transactions with SAVEPOINT
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Available at the Engine and ORM level. ORM docs so far:
-
-https://www.sqlalchemy.org/docs/04/session.html#unitofwork_managing
-
-Two-Phase Commit Sessions
-^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Available at the Engine and ORM level. ORM docs so far:
-
-https://www.sqlalchemy.org/docs/04/session.html#unitofwork_managing
-
-Inheritance
------------
-
-Polymorphic Inheritance with No Joins or Unions
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-New docs for inheritance: https://www.sqlalchemy.org/docs/04
-/mappers.html#advdatamapping_mapper_inheritance_joined
-
-Better Polymorphic Behavior with ``get()``
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-All classes within a joined-table inheritance hierarchy get
-an ``_instance_key`` using the base class, i.e.
-``(BaseClass, (1, ), None)``. That way when you call
-``get()`` a ``Query`` against the base class, it can locate
-subclass instances in the current identity map without
-querying the database.
-
-Types
------
-
-Custom Subclasses of ``sqlalchemy.types.TypeDecorator``
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-There is a `New API <https://www.sqlalchemy.org/docs/04/types
-.html#types_custom>`_ for subclassing a TypeDecorator.
-Using the 0.3 API causes compilation errors in some cases.
-
-SQL Expressions
-===============
-
-All New, Deterministic Label/Alias Generation
----------------------------------------------
-
-All the "anonymous" labels and aliases use a simple
-<name>_<number> format now. SQL is much easier to read and
-is compatible with plan optimizer caches. Just check out
-some of the examples in the tutorials:
-https://www.sqlalchemy.org/docs/04/ormtutorial.html
-https://www.sqlalchemy.org/docs/04/sqlexpression.html
-
-Generative select() Constructs
-------------------------------
-
-This is definitely the way to go with ``select()``. See htt
-p://www.sqlalchemy.org/docs/04/sqlexpression.html#sql_transf
-orm .
-
-New Operator System
--------------------
-
-SQL operators and more or less every SQL keyword there is
-are now abstracted into the compiler layer. They now act
-intelligently and are type/backend aware, see:
-https://www.sqlalchemy.org/docs/04/sqlexpression.html#sql_operators
-
-All ``type`` Keyword Arguments Renamed to ``type_``
----------------------------------------------------
-
-Just like it says:
-
-::
-
- b = bindparam("foo", type_=String)
-
-in\_ Function Changed to Accept Sequence or Selectable
-------------------------------------------------------
-
-The in\_ function now takes a sequence of values or a
-selectable as its sole argument. The previous API of passing
-in values as positional arguments still works, but is now
-deprecated. This means that
-
-::
-
- my_table.select(my_table.c.id.in_(1, 2, 3))
- my_table.select(my_table.c.id.in_(*listOfIds))
-
-should be changed to
-
-::
-
- my_table.select(my_table.c.id.in_([1, 2, 3]))
- my_table.select(my_table.c.id.in_(listOfIds))
-
-Schema and Reflection
-=====================
-
-``MetaData``, ``BoundMetaData``, ``DynamicMetaData``...
--------------------------------------------------------
-
-In the 0.3.x series, ``BoundMetaData`` and
-``DynamicMetaData`` were deprecated in favor of ``MetaData``
-and ``ThreadLocalMetaData``. The older names have been
-removed in 0.4. Updating is simple:
-
-.. sourcecode:: text
-
- +-------------------------------------+-------------------------+
- |If You Had | Now Use |
- +=====================================+=========================+
- | ``MetaData`` | ``MetaData`` |
- +-------------------------------------+-------------------------+
- | ``BoundMetaData`` | ``MetaData`` |
- +-------------------------------------+-------------------------+
- | ``DynamicMetaData`` (with one | ``MetaData`` |
- | engine or threadlocal=False) | |
- +-------------------------------------+-------------------------+
- | ``DynamicMetaData`` | ``ThreadLocalMetaData`` |
- | (with different engines per thread) | |
- +-------------------------------------+-------------------------+
-
-The seldom-used ``name`` parameter to ``MetaData`` types has
-been removed. The ``ThreadLocalMetaData`` constructor now
-takes no arguments. Both types can now be bound to an
-``Engine`` or a single ``Connection``.
-
-One Step Multi-Table Reflection
--------------------------------
-
-You can now load table definitions and automatically create
-``Table`` objects from an entire database or schema in one
-pass:
-
-::
-
- >>> metadata = MetaData(myengine, reflect=True)
- >>> metadata.tables.keys()
- ['table_a', 'table_b', 'table_c', '...']
-
-``MetaData`` also gains a ``.reflect()`` method enabling
-finer control over the loading process, including
-specification of a subset of available tables to load.
-
-SQL Execution
-=============
-
-``engine``, ``connectable``, and ``bind_to`` are all now ``bind``
------------------------------------------------------------------
-
-``Transactions``, ``NestedTransactions`` and ``TwoPhaseTransactions``
----------------------------------------------------------------------
-
-Connection Pool Events
-----------------------
-
-The connection pool now fires events when new DB-API
-connections are created, checked out and checked back into
-the pool. You can use these to execute session-scoped SQL
-setup statements on fresh connections, for example.
-
-Oracle Engine Fixed
--------------------
-
-In 0.3.11, there were bugs in the Oracle Engine on how
-Primary Keys are handled. These bugs could cause programs
-that worked fine with other engines, such as sqlite, to fail
-when using the Oracle Engine. In 0.4, the Oracle Engine has
-been reworked, fixing these Primary Key problems.
-
-Out Parameters for Oracle
--------------------------
-
-::
-
- result = engine.execute(
- text(
- "begin foo(:x, :y, :z); end;",
- bindparams=[
- bindparam("x", Numeric),
- outparam("y", Numeric),
- outparam("z", Numeric),
- ],
- ),
- x=5,
- )
- assert result.out_parameters == {"y": 10, "z": 75}
-
-Connection-bound ``MetaData``, ``Sessions``
--------------------------------------------
-
-``MetaData`` and ``Session`` can be explicitly bound to a
-connection:
-
-::
-
- conn = engine.connect()
- sess = create_session(bind=conn)
-
-Faster, More Foolproof ``ResultProxy`` Objects
-----------------------------------------------
-
+++ /dev/null
-=============================
-What's new in SQLAlchemy 0.5?
-=============================
-
-.. admonition:: About this Document
-
- This document describes changes between SQLAlchemy version 0.4,
- last released October 12, 2008, and SQLAlchemy version 0.5,
- last released January 16, 2010.
-
- Document date: August 4, 2009
-
-
-This guide documents API changes which affect users
-migrating their applications from the 0.4 series of
-SQLAlchemy to 0.5. It's also recommended for those working
-from `Essential SQLAlchemy
-<https://oreilly.com/catalog/9780596516147/>`_, which only
-covers 0.4 and seems to even have some old 0.3isms in it.
-Note that SQLAlchemy 0.5 removes many behaviors which were
-deprecated throughout the span of the 0.4 series, and also
-deprecates more behaviors specific to 0.4.
-
-Major Documentation Changes
-===========================
-
-Some sections of the documentation have been completely
-rewritten and can serve as an introduction to new ORM
-features. The ``Query`` and ``Session`` objects in
-particular have some distinct differences in API and
-behavior which fundamentally change many of the basic ways
-things are done, particularly with regards to constructing
-highly customized ORM queries and dealing with stale session
-state, commits and rollbacks.
-
-* `ORM Tutorial
- <https://www.sqlalchemy.org/docs/05/ormtutorial.html>`_
-
-* `Session Documentation
- <https://www.sqlalchemy.org/docs/05/session.html>`_
-
-Deprecations Source
-===================
-
-Another source of information is documented within a series
-of unit tests illustrating up to date usages of some common
-``Query`` patterns; this file can be viewed at
-[source:sqlalchemy/trunk/test/orm/test_deprecations.py].
-
-Requirements Changes
-====================
-
-* Python 2.4 or higher is required. The SQLAlchemy 0.4 line
- is the last version with Python 2.3 support.
-
-Object Relational Mapping
-=========================
-
-* **Column level expressions within Query.** - as detailed
- in the `tutorial
- <https://www.sqlalchemy.org/docs/05/ormtutorial.html>`_,
- ``Query`` has the capability to create specific SELECT
- statements, not just those against full rows:
-
- ::
-
- session.query(User.name, func.count(Address.id).label("numaddresses")).join(
- Address
- ).group_by(User.name)
-
- The tuples returned by any multi-column/entity query are
- *named*' tuples:
-
- ::
-
- for row in (
- session.query(User.name, func.count(Address.id).label("numaddresses"))
- .join(Address)
- .group_by(User.name)
- ):
- print("name", row.name, "number", row.numaddresses)
-
- ``Query`` has a ``statement`` accessor, as well as a
- ``subquery()`` method which allow ``Query`` to be used to
- create more complex combinations:
-
- ::
-
- subq = (
- session.query(Keyword.id.label("keyword_id"))
- .filter(Keyword.name.in_(["beans", "carrots"]))
- .subquery()
- )
- recipes = session.query(Recipe).filter(
- exists()
- .where(Recipe.id == recipe_keywords.c.recipe_id)
- .where(recipe_keywords.c.keyword_id == subq.c.keyword_id)
- )
-
-* **Explicit ORM aliases are recommended for aliased joins**
- - The ``aliased()`` function produces an "alias" of a
- class, which allows fine-grained control of aliases in
- conjunction with ORM queries. While a table-level alias
- (i.e. ``table.alias()``) is still usable, an ORM level
- alias retains the semantics of the ORM mapped object which
- is significant for inheritance mappings, options, and
- other scenarios. E.g.:
-
- ::
-
- Friend = aliased(Person)
- session.query(Person, Friend).join((Friend, Person.friends)).all()
-
-* **query.join() greatly enhanced.** - You can now specify
- the target and ON clause for a join in multiple ways. A
- target class alone can be provided where SQLA will attempt
- to form a join to it via foreign key in the same way as
- ``table.join(someothertable)``. A target and an explicit
- ON condition can be provided, where the ON condition can
- be a ``relation()`` name, an actual class descriptor, or a
- SQL expression. Or the old way of just a ``relation()``
- name or class descriptor works too. See the ORM tutorial
- which has several examples.
-
-* **Declarative is recommended for applications which don't
- require (and don't prefer) abstraction between tables and
- mappers** - The [/docs/05/reference/ext/declarative.html
- Declarative] module, which is used to combine the
- expression of ``Table``, ``mapper()``, and user defined
- class objects together, is highly recommended as it
- simplifies application configuration, ensures the "one
- mapper per class" pattern, and allows the full range of
- configuration available to distinct ``mapper()`` calls.
- Separate ``mapper()`` and ``Table`` usage is now referred
- to as "classical SQLAlchemy usage" and of course is freely
- mixable with declarative.
-
-* **The .c. attribute has been removed** from classes (i.e.
- ``MyClass.c.somecolumn``). As is the case in 0.4, class-
- level properties are usable as query elements, i.e.
- ``Class.c.propname`` is now superseded by
- ``Class.propname``, and the ``c`` attribute continues to
- remain on ``Table`` objects where they indicate the
- namespace of ``Column`` objects present on the table.
-
- To get at the Table for a mapped class (if you didn't keep
- it around already):
-
- ::
-
- table = class_mapper(someclass).mapped_table
-
- Iterate through columns:
-
- ::
-
- for col in table.c:
- print(col)
-
- Work with a specific column:
-
- ::
-
- table.c.somecolumn
-
- The class-bound descriptors support the full set of Column
- operators as well as the documented relation-oriented
- operators like ``has()``, ``any()``, ``contains()``, etc.
-
- The reason for the hard removal of ``.c.`` is that in 0.5,
- class-bound descriptors carry potentially different
- meaning, as well as information regarding class mappings,
- versus plain ``Column`` objects - and there are use cases
- where you'd specifically want to use one or the other.
- Generally, using class-bound descriptors invokes a set of
- mapping/polymorphic aware translations, and using table-
- bound columns does not. In 0.4, these translations were
- applied across the board to all expressions, but 0.5
- differentiates completely between columns and mapped
- descriptors, only applying translations to the latter. So
- in many cases, particularly when dealing with joined table
- inheritance configurations as well as when using
- ``query(<columns>)``, ``Class.propname`` and
- ``table.c.colname`` are not interchangeable.
-
- For example, ``session.query(users.c.id, users.c.name)``
- is different versus ``session.query(User.id, User.name)``;
- in the latter case, the ``Query`` is aware of the mapper
- in use and further mapper-specific operations like
- ``query.join(<propname>)``, ``query.with_parent()`` etc.
- may be used, but in the former case cannot. Additionally,
- in polymorphic inheritance scenarios, the class-bound
- descriptors refer to the columns present in the
- polymorphic selectable in use, not necessarily the table
- column which directly corresponds to the descriptor. For
- example, a set of classes related by joined-table
- inheritance to the ``person`` table along the
- ``person_id`` column of each table will all have their
- ``Class.person_id`` attribute mapped to the ``person_id``
- column in ``person``, and not their subclass table.
- Version 0.4 would map this behavior onto table-bound
- ``Column`` objects automatically. In 0.5, this automatic
- conversion has been removed, so that you in fact *can* use
- table-bound columns as a means to override the
- translations which occur with polymorphic querying; this
- allows ``Query`` to be able to create optimized selects
- among joined-table or concrete-table inheritance setups,
- as well as portable subqueries, etc.
-
-* **Session Now Synchronizes Automatically with
- Transactions.** Session now synchronizes against the
- transaction automatically by default, including autoflush
- and autoexpire. A transaction is present at all times
- unless disabled using the ``autocommit`` option. When all
- three flags are set to their default, the Session recovers
- gracefully after rollbacks and it's very difficult to get
- stale data into the session. See the new Session
- documentation for details.
-
-* **Implicit Order By Is Removed**. This will impact ORM
- users who rely upon SA's "implicit ordering" behavior,
- which states that all Query objects which don't have an
- ``order_by()`` will ORDER BY the "id" or "oid" column of
- the primary mapped table, and all lazy/eagerly loaded
- collections apply a similar ordering. In 0.5, automatic
- ordering must be explicitly configured on ``mapper()`` and
- ``relation()`` objects (if desired), or otherwise when
- using ``Query``.
-
- To convert an 0.4 mapping to 0.5, such that its ordering
- behavior will be extremely similar to 0.4 or previous, use
- the ``order_by`` setting on ``mapper()`` and
- ``relation()``:
-
- ::
-
- mapper(
- User,
- users,
- properties={"addresses": relation(Address, order_by=addresses.c.id)},
- order_by=users.c.id,
- )
-
- To set ordering on a backref, use the ``backref()``
- function:
-
- ::
-
- "keywords": relation(
- Keyword,
- secondary=item_keywords,
- order_by=keywords.c.name,
- backref=backref("items", order_by=items.c.id),
- )
-
- Using declarative ? To help with the new ``order_by``
- requirement, ``order_by`` and friends can now be set using
- strings which are evaluated in Python later on (this works
- **only** with declarative, not plain mappers):
-
- ::
-
- class MyClass(MyDeclarativeBase):
- ...
- "addresses": relation("Address", order_by="Address.id")
-
- It's generally a good idea to set ``order_by`` on
- ``relation()s`` which load list-based collections of
- items, since that ordering cannot otherwise be affected.
- Other than that, the best practice is to use
- ``Query.order_by()`` to control ordering of the primary
- entities being loaded.
-
-* **Session is now
- autoflush=True/autoexpire=True/autocommit=False.** - To
- set it up, just call ``sessionmaker()`` with no arguments.
- The name ``transactional=True`` is now
- ``autocommit=False``. Flushes occur upon each query
- issued (disable with ``autoflush=False``), within each
- ``commit()`` (as always), and before each
- ``begin_nested()`` (so rolling back to the SAVEPOINT is
- meaningful). All objects are expired after each
- ``commit()`` and after each ``rollback()``. After
- rollback, pending objects are expunged, deleted objects
- move back to persistent. These defaults work together
- very nicely and there's really no more need for old
- techniques like ``clear()`` (which is renamed to
- ``expunge_all()`` as well).
-
- P.S.: sessions are now reusable after a ``rollback()``.
- Scalar and collection attribute changes, adds and deletes
- are all rolled back.
-
-* **session.add() replaces session.save(), session.update(),
- session.save_or_update().** - the
- ``session.add(someitem)`` and ``session.add_all([list of
- items])`` methods replace ``save()``, ``update()``, and
- ``save_or_update()``. Those methods will remain
- deprecated throughout 0.5.
-
-* **backref configuration made less verbose.** - The
- ``backref()`` function now uses the ``primaryjoin`` and
- ``secondaryjoin`` arguments of the forwards-facing
- ``relation()`` when they are not explicitly stated. It's
- no longer necessary to specify
- ``primaryjoin``/``secondaryjoin`` in both directions
- separately.
-
-* **Simplified polymorphic options.** - The ORM's
- "polymorphic load" behavior has been simplified. In 0.4,
- mapper() had an argument called ``polymorphic_fetch``
- which could be configured as ``select`` or ``deferred``.
- This option is removed; the mapper will now just defer any
- columns which were not present in the SELECT statement.
- The actual SELECT statement used is controlled by the
- ``with_polymorphic`` mapper argument (which is also in 0.4
- and replaces ``select_table``), as well as the
- ``with_polymorphic()`` method on ``Query`` (also in 0.4).
-
- An improvement to the deferred loading of inheriting
- classes is that the mapper now produces the "optimized"
- version of the SELECT statement in all cases; that is, if
- class B inherits from A, and several attributes only
- present on class B have been expired, the refresh
- operation will only include B's table in the SELECT
- statement and will not JOIN to A.
-
-* The ``execute()`` method on ``Session`` converts plain
- strings into ``text()`` constructs, so that bind
- parameters may all be specified as ":bindname" without
- needing to call ``text()`` explicitly. If "raw" SQL is
- desired here, use ``session.connection().execute("raw
- text")``.
-
-* ``session.Query().iterate_instances()`` has been renamed
- to just ``instances()``. The old ``instances()`` method
- returning a list instead of an iterator no longer exists.
- If you were relying on that behavior, you should use
- ``list(your_query.instances())``.
-
-Extending the ORM
-=================
-
-In 0.5 we're moving forward with more ways to modify and
-extend the ORM. Heres a summary:
-
-* **MapperExtension.** - This is the classic extension
- class, which remains. Methods which should rarely be
- needed are ``create_instance()`` and
- ``populate_instance()``. To control the initialization of
- an object when it's loaded from the database, use the
- ``reconstruct_instance()`` method, or more easily the
- ``@reconstructor`` decorator described in the
- documentation.
-
-* **SessionExtension.** - This is an easy to use extension
- class for session events. In particular, it provides
- ``before_flush()``, ``after_flush()`` and
- ``after_flush_postexec()`` methods. This usage is
- recommended over ``MapperExtension.before_XXX`` in many
- cases since within ``before_flush()`` you can modify the
- flush plan of the session freely, something which cannot
- be done from within ``MapperExtension``.
-
-* **AttributeExtension.** - This class is now part of the
- public API, and allows the interception of userland events
- on attributes, including attribute set and delete
- operations, and collection appends and removes. It also
- allows the value to be set or appended to be modified.
- The ``@validates`` decorator, described in the
- documentation, provides a quick way to mark any mapped
- attributes as being "validated" by a particular class
- method.
-
-* **Attribute Instrumentation Customization.** - An API is
- provided for ambitious efforts to entirely replace
- SQLAlchemy's attribute instrumentation, or just to augment
- it in some cases. This API was produced for the purposes
- of the Trellis toolkit, but is available as a public API.
- Some examples are provided in the distribution in the
- ``/examples/custom_attributes`` directory.
-
-Schema/Types
-============
-
-* **String with no length no longer generates TEXT, it
- generates VARCHAR** - The ``String`` type no longer
- magically converts into a ``Text`` type when specified
- with no length. This only has an effect when CREATE TABLE
- is issued, as it will issue ``VARCHAR`` with no length
- parameter, which is not valid on many (but not all)
- databases. To create a TEXT (or CLOB, i.e. unbounded
- string) column, use the ``Text`` type.
-
-* **PickleType() with mutable=True requires an __eq__()
- method** - The ``PickleType`` type needs to compare values
- when mutable=True. The method of comparing
- ``pickle.dumps()`` is inefficient and unreliable. If an
- incoming object does not implement ``__eq__()`` and is
- also not ``None``, the ``dumps()`` comparison is used but
- a warning is raised. For types which implement
- ``__eq__()`` which includes all dictionaries, lists, etc.,
- comparison will use ``==`` and is now reliable by default.
-
-* **convert_bind_param() and convert_result_value() methods
- of TypeEngine/TypeDecorator are removed.** - The O'Reilly
- book unfortunately documented these methods even though
- they were deprecated post 0.3. For a user-defined type
- which subclasses ``TypeEngine``, the ``bind_processor()``
- and ``result_processor()`` methods should be used for
- bind/result processing. Any user defined type, whether
- extending ``TypeEngine`` or ``TypeDecorator``, which uses
- the old 0.3 style can be easily adapted to the new style
- using the following adapter:
-
- ::
-
- class AdaptOldConvertMethods(object):
- """A mixin which adapts 0.3-style convert_bind_param and
- convert_result_value methods
-
- """
-
- def bind_processor(self, dialect):
- def convert(value):
- return self.convert_bind_param(value, dialect)
-
- return convert
-
- def result_processor(self, dialect):
- def convert(value):
- return self.convert_result_value(value, dialect)
-
- return convert
-
- def convert_result_value(self, value, dialect):
- return value
-
- def convert_bind_param(self, value, dialect):
- return value
-
- To use the above mixin:
-
- ::
-
- class MyType(AdaptOldConvertMethods, TypeEngine):
- ...
-
-* The ``quote`` flag on ``Column`` and ``Table`` as well as
- the ``quote_schema`` flag on ``Table`` now control quoting
- both positively and negatively. The default is ``None``,
- meaning let regular quoting rules take effect. When
- ``True``, quoting is forced on. When ``False``, quoting
- is forced off.
-
-* Column ``DEFAULT`` value DDL can now be more conveniently
- specified with ``Column(..., server_default='val')``,
- deprecating ``Column(..., PassiveDefault('val'))``.
- ``default=`` is now exclusively for Python-initiated
- default values, and can coexist with server_default. A
- new ``server_default=FetchedValue()`` replaces the
- ``PassiveDefault('')`` idiom for marking columns as
- subject to influence from external triggers and has no DDL
- side effects.
-
-* SQLite's ``DateTime``, ``Time`` and ``Date`` types now
- **only accept datetime objects, not strings** as bind
- parameter input. If you'd like to create your own
- "hybrid" type which accepts strings and returns results as
- date objects (from whatever format you'd like), create a
- ``TypeDecorator`` that builds on ``String``. If you only
- want string-based dates, just use ``String``.
-
-* Additionally, the ``DateTime`` and ``Time`` types, when
- used with SQLite, now represent the "microseconds" field
- of the Python ``datetime.datetime`` object in the same
- manner as ``str(datetime)`` - as fractional seconds, not a
- count of microseconds. That is:
-
- ::
-
- dt = datetime.datetime(2008, 6, 27, 12, 0, 0, 125) # 125 usec
-
- # old way
- "2008-06-27 12:00:00.125"
-
- # new way
- "2008-06-27 12:00:00.000125"
-
- So if an existing SQLite file-based database intends to be
- used across 0.4 and 0.5, you either have to upgrade the
- datetime columns to store the new format (NOTE: please
- test this, I'm pretty sure its correct):
-
- .. sourcecode:: sql
-
- UPDATE mytable SET somedatecol =
- substr(somedatecol, 0, 19) || '.' || substr((substr(somedatecol, 21, -1) / 1000000), 3, -1);
-
- or, enable "legacy" mode as follows:
-
- ::
-
- from sqlalchemy.databases.sqlite import DateTimeMixin
-
- DateTimeMixin.__legacy_microseconds__ = True
-
-Connection Pool no longer threadlocal by default
-================================================
-
-0.4 has an unfortunate default setting of
-"pool_threadlocal=True", leading to surprise behavior when,
-for example, using multiple Sessions within a single thread.
-This flag is now off in 0.5. To re-enable 0.4's behavior,
-specify ``pool_threadlocal=True`` to ``create_engine()``, or
-alternatively use the "threadlocal" strategy via
-``strategy="threadlocal"``.
-
-\*args Accepted, \*args No Longer Accepted
-==========================================
-
-The policy with ``method(\*args)`` vs. ``method([args])``
-is, if the method accepts a variable-length set of items
-which represent a fixed structure, it takes ``\*args``. If
-the method accepts a variable-length set of items that are
-data-driven, it takes ``[args]``.
-
-* The various Query.options() functions ``eagerload()``,
- ``eagerload_all()``, ``lazyload()``, ``contains_eager()``,
- ``defer()``, ``undefer()`` all accept variable-length
- ``\*keys`` as their argument now, which allows a path to
- be formulated using descriptors, ie.:
-
- ::
-
- query.options(eagerload_all(User.orders, Order.items, Item.keywords))
-
- A single array argument is still accepted for backwards
- compatibility.
-
-* Similarly, the ``Query.join()`` and ``Query.outerjoin()``
- methods accept a variable length \*args, with a single
- array accepted for backwards compatibility:
-
- ::
-
- query.join("orders", "items")
- query.join(User.orders, Order.items)
-
-* the ``in_()`` method on columns and similar only accepts a
- list argument now. It no longer accepts ``\*args``.
-
-Removed
-=======
-
-* **entity_name** - This feature was always problematic and
- rarely used. 0.5's more deeply fleshed out use cases
- revealed further issues with ``entity_name`` which led to
- its removal. If different mappings are required for a
- single class, break the class into separate subclasses and
- map them separately. An example of this is at
- [wiki:UsageRecipes/EntityName]. More information
- regarding rationale is described at https://groups.google.c
- om/group/sqlalchemy/browse_thread/thread/9e23a0641a88b96d?
- hl=en .
-
-* **get()/load() cleanup**
-
-
- The ``load()`` method has been removed. Its
- functionality was kind of arbitrary and basically copied
- from Hibernate, where it's also not a particularly
- meaningful method.
-
- To get equivalent functionality:
-
- ::
-
- x = session.query(SomeClass).populate_existing().get(7)
-
- ``Session.get(cls, id)`` and ``Session.load(cls, id)``
- have been removed. ``Session.get()`` is redundant vs.
- ``session.query(cls).get(id)``.
-
- ``MapperExtension.get()`` is also removed (as is
- ``MapperExtension.load()``). To override the
- functionality of ``Query.get()``, use a subclass:
-
- ::
-
- class MyQuery(Query):
- def get(self, ident):
- ...
-
-
- session = sessionmaker(query_cls=MyQuery)()
-
- ad1 = session.query(Address).get(1)
-
-* ``sqlalchemy.orm.relation()``
-
-
- The following deprecated keyword arguments have been
- removed:
-
- foreignkey, association, private, attributeext, is_backref
-
- In particular, ``attributeext`` is replaced with
- ``extension`` - the ``AttributeExtension`` class is now in
- the public API.
-
-* ``session.Query()``
-
-
- The following deprecated functions have been removed:
-
- list, scalar, count_by, select_whereclause, get_by,
- select_by, join_by, selectfirst, selectone, select,
- execute, select_statement, select_text, join_to, join_via,
- selectfirst_by, selectone_by, apply_max, apply_min,
- apply_avg, apply_sum
-
- Additionally, the ``id`` keyword argument to ``join()``,
- ``outerjoin()``, ``add_entity()`` and ``add_column()`` has
- been removed. To target table aliases in ``Query`` to
- result columns, use the ``aliased`` construct:
-
- ::
-
- from sqlalchemy.orm import aliased
-
- address_alias = aliased(Address)
- print(session.query(User, address_alias).join((address_alias, User.addresses)).all())
-
-* ``sqlalchemy.orm.Mapper``
-
-
- * instances()
-
-
- * get_session() - this method was not very noticeable, but
- had the effect of associating lazy loads with a
- particular session even if the parent object was
- entirely detached, when an extension such as
- ``scoped_session()`` or the old ``SessionContextExt``
- was used. It's possible that some applications which
- relied upon this behavior will no longer work as
- expected; but the better programming practice here is
- to always ensure objects are present within sessions if
- database access from their attributes are required.
-
-* ``mapper(MyClass, mytable)``
-
-
- Mapped classes no are longer instrumented with a "c" class
- attribute; e.g. ``MyClass.c``
-
-* ``sqlalchemy.orm.collections``
-
-
- The _prepare_instrumentation alias for
- prepare_instrumentation has been removed.
-
-* ``sqlalchemy.orm``
-
-
- Removed the ``EXT_PASS`` alias of ``EXT_CONTINUE``.
-
-* ``sqlalchemy.engine``
-
-
- The alias from ``DefaultDialect.preexecute_sequences`` to
- ``.preexecute_pk_sequences`` has been removed.
-
- The deprecated engine_descriptors() function has been
- removed.
-
-* ``sqlalchemy.ext.activemapper``
-
-
- Module removed.
-
-* ``sqlalchemy.ext.assignmapper``
-
-
- Module removed.
-
-* ``sqlalchemy.ext.associationproxy``
-
-
- Pass-through of keyword args on the proxy's
- ``.append(item, \**kw)`` has been removed and is now
- simply ``.append(item)``
-
-* ``sqlalchemy.ext.selectresults``,
- ``sqlalchemy.mods.selectresults``
-
- Modules removed.
-
-* ``sqlalchemy.ext.declarative``
-
-
- ``declared_synonym()`` removed.
-
-* ``sqlalchemy.ext.sessioncontext``
-
-
- Module removed.
-
-* ``sqlalchemy.log``
-
-
- The ``SADeprecationWarning`` alias to
- ``sqlalchemy.exc.SADeprecationWarning`` has been removed.
-
-* ``sqlalchemy.exc``
-
-
- ``exc.AssertionError`` has been removed and usage replaced
- by the Python built-in of the same name.
-
-* ``sqlalchemy.databases.mysql``
-
-
- The deprecated ``get_version_info`` dialect method has
- been removed.
-
-Renamed or Moved
-================
-
-* ``sqlalchemy.exceptions`` is now ``sqlalchemy.exc``
-
-
- The module may still be imported under the old name until
- 0.6.
-
-* ``FlushError``, ``ConcurrentModificationError``,
- ``UnmappedColumnError`` -> sqlalchemy.orm.exc
-
- These exceptions moved to the orm package. Importing
- 'sqlalchemy.orm' will install aliases in sqlalchemy.exc
- for compatibility until 0.6.
-
-* ``sqlalchemy.logging`` -> ``sqlalchemy.log``
-
-
- This internal module was renamed. No longer needs to be
- special cased when packaging SA with py2app and similar
- tools that scan imports.
-
-* ``session.Query().iterate_instances()`` ->
- ``session.Query().instances()``.
-
-Deprecated
-==========
-
-* ``Session.save()``, ``Session.update()``,
- ``Session.save_or_update()``
-
- All three replaced by ``Session.add()``
-
-* ``sqlalchemy.PassiveDefault``
-
-
- Use ``Column(server_default=...)`` Translates to
- sqlalchemy.DefaultClause() under the hood.
-
-* ``session.Query().iterate_instances()``. It has been
- renamed to ``instances()``.
-
+++ /dev/null
-=============================
-What's New in SQLAlchemy 0.6?
-=============================
-
-.. admonition:: About this Document
-
- This document describes changes between SQLAlchemy version 0.5,
- last released January 16, 2010, and SQLAlchemy version 0.6,
- last released May 5, 2012.
-
- Document date: June 6, 2010
-
-This guide documents API changes which affect users
-migrating their applications from the 0.5 series of
-SQLAlchemy to 0.6. Note that SQLAlchemy 0.6 removes some
-behaviors which were deprecated throughout the span of the
-0.5 series, and also deprecates more behaviors specific to
-0.5.
-
-Platform Support
-================
-
-* cPython versions 2.4 and upwards throughout the 2.xx
- series
-
-* Jython 2.5.1 - using the zxJDBC DBAPI included with
- Jython.
-
-* cPython 3.x - see [source:sqlalchemy/trunk/README.py3k]
- for information on how to build for python3.
-
-New Dialect System
-==================
-
-Dialect modules are now broken up into distinct
-subcomponents, within the scope of a single database
-backend. Dialect implementations are now in the
-``sqlalchemy.dialects`` package. The
-``sqlalchemy.databases`` package still exists as a
-placeholder to provide some level of backwards compatibility
-for simple imports.
-
-For each supported database, a sub-package exists within
-``sqlalchemy.dialects`` where several files are contained.
-Each package contains a module called ``base.py`` which
-defines the specific SQL dialect used by that database. It
-also contains one or more "driver" modules, each one
-corresponding to a specific DBAPI - these files are named
-corresponding to the DBAPI itself, such as ``pysqlite``,
-``cx_oracle``, or ``pyodbc``. The classes used by
-SQLAlchemy dialects are first declared in the ``base.py``
-module, defining all behavioral characteristics defined by
-the database. These include capability mappings, such as
-"supports sequences", "supports returning", etc., type
-definitions, and SQL compilation rules. Each "driver"
-module in turn provides subclasses of those classes as
-needed which override the default behavior to accommodate
-the additional features, behaviors, and quirks of that
-DBAPI. For DBAPIs that support multiple backends (pyodbc,
-zxJDBC, mxODBC), the dialect module will use mixins from the
-``sqlalchemy.connectors`` package, which provide
-functionality common to that DBAPI across all backends, most
-typically dealing with connect arguments. This means that
-connecting using pyodbc, zxJDBC or mxODBC (when implemented)
-is extremely consistent across supported backends.
-
-The URL format used by ``create_engine()`` has been enhanced
-to handle any number of DBAPIs for a particular backend,
-using a scheme that is inspired by that of JDBC. The
-previous format still works, and will select a "default"
-DBAPI implementation, such as the PostgreSQL URL below that
-will use psycopg2:
-
-::
-
- create_engine("postgresql://scott:tiger@localhost/test")
-
-However to specify a specific DBAPI backend such as pg8000,
-add it to the "protocol" section of the URL using a plus
-sign "+":
-
-::
-
- create_engine("postgresql+pg8000://scott:tiger@localhost/test")
-
-Important Dialect Links:
-
-* Documentation on connect arguments:
- https://www.sqlalchemy.org/docs/06/dbengine.html#create-
- engine-url-arguments.
-
-* Reference documentation for individual dialects: https://ww
- w.sqlalchemy.org/docs/06/reference/dialects/index.html
-
-* The tips and tricks at DatabaseNotes.
-
-
-Other notes regarding dialects:
-
-* the type system has been changed dramatically in
- SQLAlchemy 0.6. This has an impact on all dialects
- regarding naming conventions, behaviors, and
- implementations. See the section on "Types" below.
-
-* the ``ResultProxy`` object now offers a 2x speed
- improvement in some cases thanks to some refactorings.
-
-* the ``RowProxy``, i.e. individual result row object, is
- now directly pickleable.
-
-* the setuptools entrypoint used to locate external dialects
- is now called ``sqlalchemy.dialects``. An external
- dialect written against 0.4 or 0.5 will need to be
- modified to work with 0.6 in any case so this change does
- not add any additional difficulties.
-
-* dialects now receive an initialize() event on initial
- connection to determine connection properties.
-
-* Functions and operators generated by the compiler now use
- (almost) regular dispatch functions of the form
- "visit_<opname>" and "visit_<funcname>_fn" to provide
- customed processing. This replaces the need to copy the
- "functions" and "operators" dictionaries in compiler
- subclasses with straightforward visitor methods, and also
- allows compiler subclasses complete control over
- rendering, as the full _Function or _BinaryExpression
- object is passed in.
-
-Dialect Imports
----------------
-
-The import structure of dialects has changed. Each dialect
-now exports its base "dialect" class as well as the full set
-of SQL types supported on that dialect via
-``sqlalchemy.dialects.<name>``. For example, to import a
-set of PG types:
-
-::
-
- from sqlalchemy.dialects.postgresql import (
- INTEGER,
- BIGINT,
- SMALLINT,
- VARCHAR,
- MACADDR,
- DATE,
- BYTEA,
- )
-
-Above, ``INTEGER`` is actually the plain ``INTEGER`` type
-from ``sqlalchemy.types``, but the PG dialect makes it
-available in the same way as those types which are specific
-to PG, such as ``BYTEA`` and ``MACADDR``.
-
-Expression Language Changes
-===========================
-
-An Important Expression Language Gotcha
----------------------------------------
-
-There's one quite significant behavioral change to the
-expression language which may affect some applications.
-The boolean value of Python boolean expressions, i.e.
-``==``, ``!=``, and similar, now evaluates accurately with
-regards to the two clause objects being compared.
-
-As we know, comparing a ``ClauseElement`` to any other
-object returns another ``ClauseElement``:
-
-::
-
- >>> from sqlalchemy.sql import column
- >>> column("foo") == 5
- <sqlalchemy.sql.expression._BinaryExpression object at 0x1252490>
-
-This so that Python expressions produce SQL expressions when
-converted to strings:
-
-::
-
- >>> str(column("foo") == 5)
- 'foo = :foo_1'
-
-But what happens if we say this?
-
-::
-
- >>> if column("foo") == 5:
- ... print("yes")
-
-In previous versions of SQLAlchemy, the returned
-``_BinaryExpression`` was a plain Python object which
-evaluated to ``True``. Now it evaluates to whether or not
-the actual ``ClauseElement`` should have the same hash value
-as to that being compared. Meaning:
-
-::
-
- >>> bool(column("foo") == 5)
- False
- >>> bool(column("foo") == column("foo"))
- False
- >>> c = column("foo")
- >>> bool(c == c)
- True
- >>>
-
-That means code such as the following:
-
-::
-
- if expression:
- print("the expression is:", expression)
-
-Would not evaluate if ``expression`` was a binary clause.
-Since the above pattern should never be used, the base
-``ClauseElement`` now raises an exception if called in a
-boolean context:
-
-::
-
- >>> bool(c)
- Traceback (most recent call last):
- File "<stdin>", line 1, in <module>
- ...
- raise TypeError("Boolean value of this clause is not defined")
- TypeError: Boolean value of this clause is not defined
-
-Code that wants to check for the presence of a
-``ClauseElement`` expression should instead say:
-
-::
-
- if expression is not None:
- print("the expression is:", expression)
-
-Keep in mind, **this applies to Table and Column objects
-too**.
-
-The rationale for the change is twofold:
-
-* Comparisons of the form ``if c1 == c2: <do something>``
- can actually be written now
-
-* Support for correct hashing of ``ClauseElement`` objects
- now works on alternate platforms, namely Jython. Up until
- this point SQLAlchemy relied heavily on the specific
- behavior of cPython in this regard (and still had
- occasional problems with it).
-
-Stricter "executemany" Behavior
--------------------------------
-
-An "executemany" in SQLAlchemy corresponds to a call to
-``execute()``, passing along a collection of bind parameter
-sets:
-
-::
-
- connection.execute(table.insert(), {"data": "row1"}, {"data": "row2"}, {"data": "row3"})
-
-When the ``Connection`` object sends off the given
-``insert()`` construct for compilation, it passes to the
-compiler the keynames present in the first set of binds
-passed along to determine the construction of the
-statement's VALUES clause. Users familiar with this
-construct will know that additional keys present in the
-remaining dictionaries don't have any impact. What's
-different now is that all subsequent dictionaries need to
-include at least *every* key that is present in the first
-dictionary. This means that a call like this no longer
-works:
-
-::
-
- connection.execute(
- table.insert(),
- {"timestamp": today, "data": "row1"},
- {"timestamp": today, "data": "row2"},
- {"data": "row3"},
- )
-
-Because the third row does not specify the 'timestamp'
-column. Previous versions of SQLAlchemy would simply insert
-NULL for these missing columns. However, if the
-``timestamp`` column in the above example contained a
-Python-side default value or function, it would *not* be
-used. This because the "executemany" operation is optimized
-for maximum performance across huge numbers of parameter
-sets, and does not attempt to evaluate Python-side defaults
-for those missing keys. Because defaults are often
-implemented either as SQL expressions which are embedded
-inline with the INSERT statement, or are server side
-expressions which again are triggered based on the structure
-of the INSERT string, which by definition cannot fire off
-conditionally based on each parameter set, it would be
-inconsistent for Python side defaults to behave differently
-vs. SQL/server side defaults. (SQL expression based
-defaults are embedded inline as of the 0.5 series, again to
-minimize the impact of huge numbers of parameter sets).
-
-SQLAlchemy 0.6 therefore establishes predictable consistency
-by forbidding any subsequent parameter sets from leaving any
-fields blank. That way, there's no more silent failure of
-Python side default values and functions, which additionally
-are allowed to remain consistent in their behavior versus
-SQL and server side defaults.
-
-UNION and other "compound" constructs parenthesize consistently
----------------------------------------------------------------
-
-A rule that was designed to help SQLite has been removed,
-that of the first compound element within another compound
-(such as, a ``union()`` inside of an ``except_()``) wouldn't
-be parenthesized. This is inconsistent and produces the
-wrong results on PostgreSQL, which has precedence rules
-regarding INTERSECTION, and its generally a surprise. When
-using complex composites with SQLite, you now need to turn
-the first element into a subquery (which is also compatible
-on PG). A new example is in the SQL expression tutorial at
-the end of
-[https://www.sqlalchemy.org/docs/06/sqlexpression.html
-#unions-and-other-set-operations]. See :ticket:`1665` and
-r6690 for more background.
-
-C Extensions for Result Fetching
-================================
-
-The ``ResultProxy`` and related elements, including most
-common "row processing" functions such as unicode
-conversion, numerical/boolean conversions and date parsing,
-have been re-implemented as optional C extensions for the
-purposes of performance. This represents the beginning of
-SQLAlchemy's path to the "dark side" where we hope to
-continue improving performance by reimplementing critical
-sections in C. The extensions can be built by specifying
-``--with-cextensions``, i.e. ``python setup.py --with-
-cextensions install``.
-
-The extensions have the most dramatic impact on result
-fetching using direct ``ResultProxy`` access, i.e. that
-which is returned by ``engine.execute()``,
-``connection.execute()``, or ``session.execute()``. Within
-results returned by an ORM ``Query`` object, result fetching
-is not as high a percentage of overhead, so ORM performance
-improves more modestly, and mostly in the realm of fetching
-large result sets. The performance improvements highly
-depend on the dbapi in use and on the syntax used to access
-the columns of each row (eg ``row['name']`` is much faster
-than ``row.name``). The current extensions have no impact
-on the speed of inserts/updates/deletes, nor do they improve
-the latency of SQL execution, that is, an application that
-spends most of its time executing many statements with very
-small result sets will not see much improvement.
-
-Performance has been improved in 0.6 versus 0.5 regardless
-of the extensions. A quick overview of what connecting and
-fetching 50,000 rows looks like with SQLite, using mostly
-direct SQLite access, a ``ResultProxy``, and a simple mapped
-ORM object:
-
-.. sourcecode:: text
-
- sqlite select/native: 0.260s
-
- 0.6 / C extension
-
- sqlalchemy.sql select: 0.360s
- sqlalchemy.orm fetch: 2.500s
-
- 0.6 / Pure Python
-
- sqlalchemy.sql select: 0.600s
- sqlalchemy.orm fetch: 3.000s
-
- 0.5 / Pure Python
-
- sqlalchemy.sql select: 0.790s
- sqlalchemy.orm fetch: 4.030s
-
-Above, the ORM fetches the rows 33% faster than 0.5 due to
-in-python performance enhancements. With the C extensions
-we get another 20%. However, ``ResultProxy`` fetches
-improve by 67% with the C extension versus not. Other
-tests report as much as a 200% speed improvement for some
-scenarios, such as those where lots of string conversions
-are occurring.
-
-New Schema Capabilities
-=======================
-
-The ``sqlalchemy.schema`` package has received some long-
-needed attention. The most visible change is the newly
-expanded DDL system. In SQLAlchemy, it was possible since
-version 0.5 to create custom DDL strings and associate them
-with tables or metadata objects:
-
-::
-
- from sqlalchemy.schema import DDL
-
- DDL("CREATE TRIGGER users_trigger ...").execute_at("after-create", metadata)
-
-Now the full suite of DDL constructs are available under the
-same system, including those for CREATE TABLE, ADD
-CONSTRAINT, etc.:
-
-::
-
- from sqlalchemy.schema import Constraint, AddConstraint
-
- AddContraint(CheckConstraint("value > 5")).execute_at("after-create", mytable)
-
-Additionally, all the DDL objects are now regular
-``ClauseElement`` objects just like any other SQLAlchemy
-expression object:
-
-::
-
- from sqlalchemy.schema import CreateTable
-
- create = CreateTable(mytable)
-
- # dumps the CREATE TABLE as a string
- print(create)
-
- # executes the CREATE TABLE statement
- engine.execute(create)
-
-and using the ``sqlalchemy.ext.compiler`` extension you can
-make your own:
-
-::
-
- from sqlalchemy.schema import DDLElement
- from sqlalchemy.ext.compiler import compiles
-
-
- class AlterColumn(DDLElement):
- def __init__(self, column, cmd):
- self.column = column
- self.cmd = cmd
-
-
- @compiles(AlterColumn)
- def visit_alter_column(element, compiler, **kw):
- return "ALTER TABLE %s ALTER COLUMN %s %s ..." % (
- element.column.table.name,
- element.column.name,
- element.cmd,
- )
-
-
- engine.execute(AlterColumn(table.c.mycolumn, "SET DEFAULT 'test'"))
-
-Deprecated/Removed Schema Elements
-----------------------------------
-
-The schema package has also been greatly streamlined. Many
-options and methods which were deprecated throughout 0.5
-have been removed. Other little known accessors and methods
-have also been removed.
-
-* the "owner" keyword argument is removed from ``Table``.
- Use "schema" to represent any namespaces to be prepended
- to the table name.
-
-* deprecated ``MetaData.connect()`` and
- ``ThreadLocalMetaData.connect()`` have been removed - send
- the "bind" attribute to bind a metadata.
-
-* deprecated metadata.table_iterator() method removed (use
- sorted_tables)
-
-* the "metadata" argument is removed from
- ``DefaultGenerator`` and subclasses, but remains locally
- present on ``Sequence``, which is a standalone construct
- in DDL.
-
-* deprecated ``PassiveDefault`` - use ``DefaultClause``.
-
-
-* Removed public mutability from ``Index`` and
- ``Constraint`` objects:
-
- * ``ForeignKeyConstraint.append_element()``
-
-
- * ``Index.append_column()``
-
-
- * ``UniqueConstraint.append_column()``
-
-
- * ``PrimaryKeyConstraint.add()``
-
-
- * ``PrimaryKeyConstraint.remove()``
-
-
-These should be constructed declaratively (i.e. in one
-construction).
-
-* Other removed things:
-
-
- * ``Table.key`` (no idea what this was for)
-
-
- * ``Column.bind`` (get via column.table.bind)
-
-
- * ``Column.metadata`` (get via column.table.metadata)
-
-
- * ``Column.sequence`` (use column.default)
-
-
-Other Behavioral Changes
-------------------------
-
-* ``UniqueConstraint``, ``Index``, ``PrimaryKeyConstraint``
- all accept lists of column names or column objects as
- arguments.
-
-* The ``use_alter`` flag on ``ForeignKey`` is now a shortcut
- option for operations that can be hand-constructed using
- the ``DDL()`` event system. A side effect of this refactor
- is that ``ForeignKeyConstraint`` objects with
- ``use_alter=True`` will *not* be emitted on SQLite, which
- does not support ALTER for foreign keys. This has no
- effect on SQLite's behavior since SQLite does not actually
- honor FOREIGN KEY constraints.
-
-* ``Table.primary_key`` is not assignable - use
- ``table.append_constraint(PrimaryKeyConstraint(...))``
-
-* A ``Column`` definition with a ``ForeignKey`` and no type,
- e.g. ``Column(name, ForeignKey(sometable.c.somecol))``
- used to get the type of the referenced column. Now support
- for that automatic type inference is partial and may not
- work in all cases.
-
-Logging opened up
-=================
-
-At the expense of a few extra method calls here and there,
-you can set log levels for INFO and DEBUG after an engine,
-pool, or mapper has been created, and logging will commence.
-The ``isEnabledFor(INFO)`` method is now called
-per-``Connection`` and ``isEnabledFor(DEBUG)``
-per-``ResultProxy`` if already enabled on the parent
-connection. Pool logging sends to ``log.info()`` and
-``log.debug()`` with no check - note that pool
-checkout/checkin is typically once per transaction.
-
-Reflection/Inspector API
-========================
-
-The reflection system, which allows reflection of table
-columns via ``Table('sometable', metadata, autoload=True)``
-has been opened up into its own fine-grained API, which
-allows direct inspection of database elements such as
-tables, columns, constraints, indexes, and more. This API
-expresses return values as simple lists of strings,
-dictionaries, and ``TypeEngine`` objects. The internals of
-``autoload=True`` now build upon this system such that the
-translation of raw database information into
-``sqlalchemy.schema`` constructs is centralized and the
-contract of individual dialects greatly simplified, vastly
-reducing bugs and inconsistencies across different backends.
-
-To use an inspector:
-
-::
-
- from sqlalchemy.engine.reflection import Inspector
-
- insp = Inspector.from_engine(my_engine)
-
- print(insp.get_schema_names())
-
-the ``from_engine()`` method will in some cases provide a
-backend-specific inspector with additional capabilities,
-such as that of PostgreSQL which provides a
-``get_table_oid()`` method:
-
-::
-
-
- my_engine = create_engine("postgresql://...")
- pg_insp = Inspector.from_engine(my_engine)
-
- print(pg_insp.get_table_oid("my_table"))
-
-RETURNING Support
-=================
-
-The ``insert()``, ``update()`` and ``delete()`` constructs
-now support a ``returning()`` method, which corresponds to
-the SQL RETURNING clause as supported by PostgreSQL, Oracle,
-MS-SQL, and Firebird. It is not supported for any other
-backend at this time.
-
-Given a list of column expressions in the same manner as
-that of a ``select()`` construct, the values of these
-columns will be returned as a regular result set:
-
-::
-
-
- result = connection.execute(
- table.insert().values(data="some data").returning(table.c.id, table.c.timestamp)
- )
- row = result.first()
- print("ID:", row["id"], "Timestamp:", row["timestamp"])
-
-The implementation of RETURNING across the four supported
-backends varies wildly, in the case of Oracle requiring an
-intricate usage of OUT parameters which are re-routed into a
-"mock" result set, and in the case of MS-SQL using an
-awkward SQL syntax. The usage of RETURNING is subject to
-limitations:
-
-* it does not work for any "executemany()" style of
- execution. This is a limitation of all supported DBAPIs.
-
-* Some backends, such as Oracle, only support RETURNING that
- returns a single row - this includes UPDATE and DELETE
- statements, meaning the update() or delete() construct
- must match only a single row, or an error is raised (by
- Oracle, not SQLAlchemy).
-
-RETURNING is also used automatically by SQLAlchemy, when
-available and when not otherwise specified by an explicit
-``returning()`` call, to fetch the value of newly generated
-primary key values for single-row INSERT statements. This
-means there's no more "SELECT nextval(sequence)" pre-
-execution for insert statements where the primary key value
-is required. Truth be told, implicit RETURNING feature
-does incur more method overhead than the old "select
-nextval()" system, which used a quick and dirty
-cursor.execute() to get at the sequence value, and in the
-case of Oracle requires additional binding of out
-parameters. So if method/protocol overhead is proving to be
-more expensive than additional database round trips, the
-feature can be disabled by specifying
-``implicit_returning=False`` to ``create_engine()``.
-
-Type System Changes
-===================
-
-New Architecture
-----------------
-
-The type system has been completely reworked behind the
-scenes to provide two goals:
-
-* Separate the handling of bind parameters and result row
- values, typically a DBAPI requirement, from the SQL
- specification of the type itself, which is a database
- requirement. This is consistent with the overall dialect
- refactor that separates database SQL behavior from DBAPI.
-
-* Establish a clear and consistent contract for generating
- DDL from a ``TypeEngine`` object and for constructing
- ``TypeEngine`` objects based on column reflection.
-
-Highlights of these changes include:
-
-* The construction of types within dialects has been totally
- overhauled. Dialects now define publicly available types
- as UPPERCASE names exclusively, and internal
- implementation types using underscore identifiers (i.e.
- are private). The system by which types are expressed in
- SQL and DDL has been moved to the compiler system. This
- has the effect that there are much fewer type objects
- within most dialects. A detailed document on this
- architecture for dialect authors is in [source:/lib/sqlalc
- hemy/dialects/type_migration_guidelines.txt].
-
-* Reflection of types now returns the exact UPPERCASE type
- within types.py, or the UPPERCASE type within the dialect
- itself if the type is not a standard SQL type. This means
- reflection now returns more accurate information about
- reflected types.
-
-* User defined types that subclass ``TypeEngine`` and wish
- to provide ``get_col_spec()`` should now subclass
- ``UserDefinedType``.
-
-* The ``result_processor()`` method on all type classes now
- accepts an additional argument ``coltype``. This is the
- DBAPI type object attached to cursor.description, and
- should be used when applicable to make better decisions on
- what kind of result-processing callable should be
- returned. Ideally result processor functions would never
- need to use ``isinstance()``, which is an expensive call
- at this level.
-
-Native Unicode Mode
--------------------
-
-As more DBAPIs support returning Python unicode objects
-directly, the base dialect now performs a check upon the
-first connection which establishes whether or not the DBAPI
-returns a Python unicode object for a basic select of a
-VARCHAR value. If so, the ``String`` type and all
-subclasses (i.e. ``Text``, ``Unicode``, etc.) will skip the
-"unicode" check/conversion step when result rows are
-received. This offers a dramatic performance increase for
-large result sets. The "unicode mode" currently is known to
-work with:
-
-* sqlite3 / pysqlite
-
-
-* psycopg2 - SQLA 0.6 now uses the "UNICODE" type extension
- by default on each psycopg2 connection object
-
-* pg8000
-
-
-* cx_oracle (we use an output processor - nice feature !)
-
-
-Other types may choose to disable unicode processing as
-needed, such as the ``NVARCHAR`` type when used with MS-SQL.
-
-In particular, if porting an application based on a DBAPI
-that formerly returned non-unicode strings, the "native
-unicode" mode has a plainly different default behavior -
-columns that are declared as ``String`` or ``VARCHAR`` now
-return unicode by default whereas they would return strings
-before. This can break code which expects non-unicode
-strings. The psycopg2 "native unicode" mode can be
-disabled by passing ``use_native_unicode=False`` to
-``create_engine()``.
-
-A more general solution for string columns that explicitly
-do not want a unicode object is to use a ``TypeDecorator``
-that converts unicode back to utf-8, or whatever is desired:
-
-::
-
- class UTF8Encoded(TypeDecorator):
- """Unicode type which coerces to utf-8."""
-
- impl = sa.VARCHAR
-
- def process_result_value(self, value, dialect):
- if isinstance(value, unicode):
- value = value.encode("utf-8")
- return value
-
-Note that the ``assert_unicode`` flag is now deprecated.
-SQLAlchemy allows the DBAPI and backend database in use to
-handle Unicode parameters when available, and does not add
-operational overhead by checking the incoming type; modern
-systems like sqlite and PostgreSQL will raise an encoding
-error on their end if invalid data is passed. In those
-cases where SQLAlchemy does need to coerce a bind parameter
-from Python Unicode to an encoded string, or when the
-Unicode type is used explicitly, a warning is raised if the
-object is a bytestring. This warning can be suppressed or
-converted to an exception using the Python warnings filter
-documented at: https://docs.python.org/library/warnings.html
-
-Generic Enum Type
------------------
-
-We now have an ``Enum`` in the ``types`` module. This is a
-string type that is given a collection of "labels" which
-constrain the possible values given to those labels. By
-default, this type generates a ``VARCHAR`` using the size of
-the largest label, and applies a CHECK constraint to the
-table within the CREATE TABLE statement. When using MySQL,
-the type by default uses MySQL's ENUM type, and when using
-PostgreSQL the type will generate a user defined type using
-``CREATE TYPE <mytype> AS ENUM``. In order to create the
-type using PostgreSQL, the ``name`` parameter must be
-specified to the constructor. The type also accepts a
-``native_enum=False`` option which will issue the
-VARCHAR/CHECK strategy for all databases. Note that
-PostgreSQL ENUM types currently don't work with pg8000 or
-zxjdbc.
-
-Reflection Returns Dialect-Specific Types
------------------------------------------
-
-Reflection now returns the most specific type possible from
-the database. That is, if you create a table using
-``String``, then reflect it back, the reflected column will
-likely be ``VARCHAR``. For dialects that support a more
-specific form of the type, that's what you'll get. So a
-``Text`` type would come back as ``oracle.CLOB`` on Oracle,
-a ``LargeBinary`` might be an ``mysql.MEDIUMBLOB`` etc. The
-obvious advantage here is that reflection preserves as much
-information possible from what the database had to say.
-
-Some applications that deal heavily in table metadata may
-wish to compare types across reflected tables and/or non-
-reflected tables. There's a semi-private accessor available
-on ``TypeEngine`` called ``_type_affinity`` and an
-associated comparison helper ``_compare_type_affinity``.
-This accessor returns the "generic" ``types`` class which
-the type corresponds to:
-
-::
-
- >>> String(50)._compare_type_affinity(postgresql.VARCHAR(50))
- True
- >>> Integer()._compare_type_affinity(mysql.REAL)
- False
-
-Miscellaneous API Changes
--------------------------
-
-The usual "generic" types are still the general system in
-use, i.e. ``String``, ``Float``, ``DateTime``. There's a
-few changes there:
-
-* Types no longer make any guesses as to default parameters.
- In particular, ``Numeric``, ``Float``, as well as
- subclasses NUMERIC, FLOAT, DECIMAL don't generate any
- length or scale unless specified. This also continues to
- include the controversial ``String`` and ``VARCHAR`` types
- (although MySQL dialect will pre-emptively raise when
- asked to render VARCHAR with no length). No defaults are
- assumed, and if they are used in a CREATE TABLE statement,
- an error will be raised if the underlying database does
- not allow non-lengthed versions of these types.
-
-* the ``Binary`` type has been renamed to ``LargeBinary``,
- for BLOB/BYTEA/similar types. For ``BINARY`` and
- ``VARBINARY``, those are present directly as
- ``types.BINARY``, ``types.VARBINARY``, as well as in the
- MySQL and MS-SQL dialects.
-
-* ``PickleType`` now uses == for comparison of values when
- mutable=True, unless the "comparator" argument with a
- comparison function is specified to the type. If you are
- pickling a custom object you should implement an
- ``__eq__()`` method so that value-based comparisons are
- accurate.
-
-* The default "precision" and "scale" arguments of Numeric
- and Float have been removed and now default to None.
- NUMERIC and FLOAT will be rendered with no numeric
- arguments by default unless these values are provided.
-
-* DATE, TIME and DATETIME types on SQLite can now take
- optional "storage_format" and "regexp" argument.
- "storage_format" can be used to store those types using a
- custom string format. "regexp" allows to use a custom
- regular expression to match string values from the
- database.
-
-* ``__legacy_microseconds__`` on SQLite ``Time`` and
- ``DateTime`` types is not supported anymore. You should
- use the new "storage_format" argument instead.
-
-* ``DateTime`` types on SQLite now use by a default a
- stricter regular expression to match strings from the
- database. Use the new "regexp" argument if you are using
- data stored in a legacy format.
-
-ORM Changes
-===========
-
-Upgrading an ORM application from 0.5 to 0.6 should require
-little to no changes, as the ORM's behavior remains almost
-identical. There are some default argument and name
-changes, and some loading behaviors have been improved.
-
-New Unit of Work
-----------------
-
-The internals for the unit of work, primarily
-``topological.py`` and ``unitofwork.py``, have been
-completely rewritten and are vastly simplified. This
-should have no impact on usage, as all existing behavior
-during flush has been maintained exactly (or at least, as
-far as it is exercised by our testsuite and the handful of
-production environments which have tested it heavily). The
-performance of flush() now uses 20-30% fewer method calls
-and should also use less memory. The intent and flow of the
-source code should now be reasonably easy to follow, and the
-architecture of the flush is fairly open-ended at this
-point, creating room for potential new areas of
-sophistication. The flush process no longer has any
-reliance on recursion so flush plans of arbitrary size and
-complexity can be flushed. Additionally, the mapper's
-"save" process, which issues INSERT and UPDATE statements,
-now caches the "compiled" form of the two statements so that
-callcounts are further dramatically reduced with very large
-flushes.
-
-Any changes in behavior observed with flush versus earlier
-versions of 0.6 or 0.5 should be reported to us ASAP - we'll
-make sure no functionality is lost.
-
-Changes to ``query.update()`` and ``query.delete()``
-----------------------------------------------------
-
-* the 'expire' option on query.update() has been renamed to
- 'fetch', thus matching that of query.delete()
-
-* ``query.update()`` and ``query.delete()`` both default to
- 'evaluate' for the synchronize strategy.
-
-* the 'synchronize' strategy for update() and delete()
- raises an error on failure. There is no implicit fallback
- onto "fetch". Failure of evaluation is based on the
- structure of criteria, so success/failure is deterministic
- based on code structure.
-
-``relation()`` is officially named ``relationship()``
------------------------------------------------------
-
-This to solve the long running issue that "relation" means a
-"table or derived table" in relational algebra terms. The
-``relation()`` name, which is less typing, will hang around
-for the foreseeable future so this change should be entirely
-painless.
-
-Subquery eager loading
-----------------------
-
-A new kind of eager loading is added called "subquery"
-loading. This is a load that emits a second SQL query
-immediately after the first which loads full collections for
-all the parents in the first query, joining upwards to the
-parent using INNER JOIN. Subquery loading is used similarly
-to the current joined-eager loading, using the
-```subqueryload()```` and ````subqueryload_all()```` options
-as well as the ````lazy='subquery'```` setting on
-````relationship()```. The subquery load is usually much
-more efficient for loading many larger collections as it
-uses INNER JOIN unconditionally and also doesn't re-load
-parent rows.
-
-```eagerload()````, ````eagerload_all()```` is now ````joinedload()````, ````joinedload_all()```
-------------------------------------------------------------------------------------------------
-
-To make room for the new subquery load feature, the existing
-```eagerload()````/````eagerload_all()```` options are now
-superseded by ````joinedload()```` and
-````joinedload_all()````. The old names will hang around
-for the foreseeable future just like ````relation()```.
-
-```lazy=False|None|True|'dynamic'```` now accepts ````lazy='noload'|'joined'|'subquery'|'select'|'dynamic'```
--------------------------------------------------------------------------------------------------------------
-
-Continuing on the theme of loader strategies opened up, the
-standard keywords for the ```lazy```` option on
-````relationship()```` are now ````select```` for lazy
-loading (via a SELECT issued on attribute access),
-````joined```` for joined-eager loading, ````subquery````
-for subquery-eager loading, ````noload```` for no loading
-should occur, and ````dynamic```` for a "dynamic"
-relationship. The old ````True````, ````False````,
-````None``` arguments are still accepted with the identical
-behavior as before.
-
-innerjoin=True on relation, joinedload
---------------------------------------
-
-Joined-eagerly loaded scalars and collections can now be
-instructed to use INNER JOIN instead of OUTER JOIN. On
-PostgreSQL this is observed to provide a 300-600% speedup on
-some queries. Set this flag for any many-to-one which is
-on a NOT NULLable foreign key, and similarly for any
-collection where related items are guaranteed to exist.
-
-At mapper level:
-
-::
-
- mapper(Child, child)
- mapper(
- Parent,
- parent,
- properties={"child": relationship(Child, lazy="joined", innerjoin=True)},
- )
-
-At query time level:
-
-::
-
- session.query(Parent).options(joinedload(Parent.child, innerjoin=True)).all()
-
-The ``innerjoin=True`` flag at the ``relationship()`` level
-will also take effect for any ``joinedload()`` option which
-does not override the value.
-
-Many-to-one Enhancements
-------------------------
-
-* many-to-one relations now fire off a lazyload in fewer
- cases, including in most cases will not fetch the "old"
- value when a new one is replaced.
-
-* many-to-one relation to a joined-table subclass now uses
- get() for a simple load (known as the "use_get"
- condition), i.e. ``Related``->``Sub(Base)``, without the
- need to redefine the primaryjoin condition in terms of the
- base table. [ticket:1186]
-
-* specifying a foreign key with a declarative column, i.e.
- ``ForeignKey(MyRelatedClass.id)`` doesn't break the
- "use_get" condition from taking place [ticket:1492]
-
-* relationship(), joinedload(), and joinedload_all() now
- feature an option called "innerjoin". Specify ``True`` or
- ``False`` to control whether an eager join is constructed
- as an INNER or OUTER join. Default is ``False`` as always.
- The mapper options will override whichever setting is
- specified on relationship(). Should generally be set for
- many-to-one, not nullable foreign key relations to allow
- improved join performance. [ticket:1544]
-
-* the behavior of joined eager loading such that the main
- query is wrapped in a subquery when LIMIT/OFFSET are
- present now makes an exception for the case when all eager
- loads are many-to-one joins. In those cases, the eager
- joins are against the parent table directly along with the
- limit/offset without the extra overhead of a subquery,
- since a many-to-one join does not add rows to the result.
-
- For example, in 0.5 this query:
-
- ::
-
- session.query(Address).options(eagerload(Address.user)).limit(10)
-
- would produce SQL like:
-
- .. sourcecode:: sql
-
- SELECT * FROM
- (SELECT * FROM addresses LIMIT 10) AS anon_1
- LEFT OUTER JOIN users AS users_1 ON users_1.id = anon_1.addresses_user_id
-
- This because the presence of any eager loaders suggests
- that some or all of them may relate to multi-row
- collections, which would necessitate wrapping any kind of
- rowcount-sensitive modifiers like LIMIT inside of a
- subquery.
-
- In 0.6, that logic is more sensitive and can detect if all
- eager loaders represent many-to-ones, in which case the
- eager joins don't affect the rowcount:
-
- .. sourcecode:: sql
-
- SELECT * FROM addresses LEFT OUTER JOIN users AS users_1 ON users_1.id = addresses.user_id LIMIT 10
-
-Mutable Primary Keys with Joined Table Inheritance
---------------------------------------------------
-
-A joined table inheritance config where the child table has
-a PK that foreign keys to the parent PK can now be updated
-on a CASCADE-capable database like PostgreSQL.
-``mapper()`` now has an option ``passive_updates=True``
-which indicates this foreign key is updated automatically.
-If on a non-cascading database like SQLite or MySQL/MyISAM,
-set this flag to ``False``. A future feature enhancement
-will try to get this flag to be auto-configuring based on
-dialect/table style in use.
-
-Beaker Caching
---------------
-
-A promising new example of Beaker integration is in
-``examples/beaker_caching``. This is a straightforward
-recipe which applies a Beaker cache within the result-
-generation engine of ``Query``. Cache parameters are
-provided via ``query.options()``, and allows full control
-over the contents of the cache. SQLAlchemy 0.6 includes
-improvements to the ``Session.merge()`` method to support
-this and similar recipes, as well as to provide
-significantly improved performance in most scenarios.
-
-Other Changes
--------------
-
-* the "row tuple" object returned by ``Query`` when multiple
- column/entities are selected is now picklable as well as
- higher performing.
-
-* ``query.join()`` has been reworked to provide more
- consistent behavior and more flexibility (includes
- [ticket:1537])
-
-* ``query.select_from()`` accepts multiple clauses to
- produce multiple comma separated entries within the FROM
- clause. Useful when selecting from multiple-homed join()
- clauses.
-
-* the "dont_load=True" flag on ``Session.merge()`` is
- deprecated and is now "load=False".
-
-* added "make_transient()" helper function which transforms
- a persistent/ detached instance into a transient one (i.e.
- deletes the instance_key and removes from any session.)
- [ticket:1052]
-
-* the allow_null_pks flag on mapper() is deprecated and has
- been renamed to allow_partial_pks. It is turned "on" by
- default. This means that a row which has a non-null value
- for any of its primary key columns will be considered an
- identity. The need for this scenario typically only occurs
- when mapping to an outer join. When set to False, a PK
- that has NULLs in it will not be considered a primary key
- - in particular this means a result row will come back as
- None (or not be filled into a collection), and new in 0.6
- also indicates that session.merge() won't issue a round
- trip to the database for such a PK value. [ticket:1680]
-
-* the mechanics of "backref" have been fully merged into the
- finer grained "back_populates" system, and take place
- entirely within the ``_generate_backref()`` method of
- ``RelationProperty``. This makes the initialization
- procedure of ``RelationProperty`` simpler and allows
- easier propagation of settings (such as from subclasses of
- ``RelationProperty``) into the reverse reference. The
- internal ``BackRef()`` is gone and ``backref()`` returns a
- plain tuple that is understood by ``RelationProperty``.
-
-* the keys attribute of ``ResultProxy`` is now a method, so
- references to it (``result.keys``) must be changed to
- method invocations (``result.keys()``)
-
-* ``ResultProxy.last_inserted_ids`` is now deprecated, use
- ``ResultProxy.inserted_primary_key`` instead.
-
-Deprecated/Removed ORM Elements
--------------------------------
-
-Most elements that were deprecated throughout 0.5 and raised
-deprecation warnings have been removed (with a few
-exceptions). All elements that were marked "pending
-deprecation" are now deprecated and will raise a warning
-upon use.
-
-* 'transactional' flag on sessionmaker() and others is
- removed. Use 'autocommit=True' to indicate
- 'transactional=False'.
-
-* 'polymorphic_fetch' argument on mapper() is removed.
- Loading can be controlled using the 'with_polymorphic'
- option.
-
-* 'select_table' argument on mapper() is removed. Use
- 'with_polymorphic=("*", <some selectable>)' for this
- functionality.
-
-* 'proxy' argument on synonym() is removed. This flag did
- nothing throughout 0.5, as the "proxy generation"
- behavior is now automatic.
-
-* Passing a single list of elements to joinedload(),
- joinedload_all(), contains_eager(), lazyload(), defer(),
- and undefer() instead of multiple positional \*args is
- deprecated.
-
-* Passing a single list of elements to query.order_by(),
- query.group_by(), query.join(), or query.outerjoin()
- instead of multiple positional \*args is deprecated.
-
-* ``query.iterate_instances()`` is removed. Use
- ``query.instances()``.
-
-* ``Query.query_from_parent()`` is removed. Use the
- sqlalchemy.orm.with_parent() function to produce a
- "parent" clause, or alternatively ``query.with_parent()``.
-
-* ``query._from_self()`` is removed, use
- ``query.from_self()`` instead.
-
-* the "comparator" argument to composite() is removed. Use
- "comparator_factory".
-
-* ``RelationProperty._get_join()`` is removed.
-
-
-* the 'echo_uow' flag on Session is removed. Use logging
- on the "sqlalchemy.orm.unitofwork" name.
-
-* ``session.clear()`` is removed. use
- ``session.expunge_all()``.
-
-* ``session.save()``, ``session.update()``,
- ``session.save_or_update()`` are removed. Use
- ``session.add()`` and ``session.add_all()``.
-
-* the "objects" flag on session.flush() remains deprecated.
-
-
-* the "dont_load=True" flag on session.merge() is deprecated
- in favor of "load=False".
-
-* ``ScopedSession.mapper`` remains deprecated. See the
- usage recipe at https://www.sqlalchemy.org/trac/wiki/Usag
- eRecipes/SessionAwareMapper
-
-* passing an ``InstanceState`` (internal SQLAlchemy state
- object) to ``attributes.init_collection()`` or
- ``attributes.get_history()`` is deprecated. These
- functions are public API and normally expect a regular
- mapped object instance.
-
-* the 'engine' parameter to ``declarative_base()`` is
- removed. Use the 'bind' keyword argument.
-
-Extensions
-==========
-
-SQLSoup
--------
-
-SQLSoup has been modernized and updated to reflect common
-0.5/0.6 capabilities, including well defined session
-integration. Please read the new docs at [https://www.sqlalc
-hemy.org/docs/06/reference/ext/sqlsoup.html].
-
-Declarative
------------
-
-The ``DeclarativeMeta`` (default metaclass for
-``declarative_base``) previously allowed subclasses to
-modify ``dict_`` to add class attributes (e.g. columns).
-This no longer works, the ``DeclarativeMeta`` constructor
-now ignores ``dict_``. Instead, the class attributes should
-be assigned directly, e.g. ``cls.id=Column(...)``, or the
-`MixIn class <https://www.sqlalchemy.org/docs/reference/ext/d
-eclarative.html#mix-in-classes>`_ approach should be used
-instead of the metaclass approach.
-
+++ /dev/null
-=============================
-What's New in SQLAlchemy 0.7?
-=============================
-
-.. admonition:: About this Document
-
- This document describes changes between SQLAlchemy version 0.6,
- last released May 5, 2012, and SQLAlchemy version 0.7,
- undergoing maintenance releases as of October, 2012.
-
- Document date: July 27, 2011
-
-Introduction
-============
-
-This guide introduces what's new in SQLAlchemy version 0.7,
-and also documents changes which affect users migrating
-their applications from the 0.6 series of SQLAlchemy to 0.7.
-
-To as great a degree as possible, changes are made in such a
-way as to not break compatibility with applications built
-for 0.6. The changes that are necessarily not backwards
-compatible are very few, and all but one, the change to
-mutable attribute defaults, should affect an exceedingly
-small portion of applications - many of the changes regard
-non-public APIs and undocumented hacks some users may have
-been attempting to use.
-
-A second, even smaller class of non-backwards-compatible
-changes is also documented. This class of change regards
-those features and behaviors that have been deprecated at
-least since version 0.5 and have been raising warnings since
-their deprecation. These changes would only affect
-applications that are still using 0.4- or early 0.5-style
-APIs. As the project matures, we have fewer and fewer of
-these kinds of changes with 0.x level releases, which is a
-product of our API having ever fewer features that are less
-than ideal for the use cases they were meant to solve.
-
-An array of existing functionalities have been superseded in
-SQLAlchemy 0.7. There's not much difference between the
-terms "superseded" and "deprecated", except that the former
-has a much weaker suggestion of the old feature would ever
-be removed. In 0.7, features like ``synonym`` and
-``comparable_property``, as well as all the ``Extension``
-and other event classes, have been superseded. But these
-"superseded" features have been re-implemented such that
-their implementations live mostly outside of core ORM code,
-so their continued "hanging around" doesn't impact
-SQLAlchemy's ability to further streamline and refine its
-internals, and we expect them to remain within the API for
-the foreseeable future.
-
-New Features
-============
-
-New Event System
-----------------
-
-SQLAlchemy started early with the ``MapperExtension`` class,
-which provided hooks into the persistence cycle of mappers.
-As SQLAlchemy quickly became more componentized, pushing
-mappers into a more focused configurational role, many more
-"extension", "listener", and "proxy" classes popped up to
-solve various activity-interception use cases in an ad-hoc
-fashion. Part of this was driven by the divergence of
-activities; ``ConnectionProxy`` objects wanted to provide a
-system of rewriting statements and parameters;
-``AttributeExtension`` provided a system of replacing
-incoming values, and ``DDL`` objects had events that could
-be switched off of dialect-sensitive callables.
-
-0.7 re-implements virtually all of these plugin points with
-a new, unified approach, which retains all the
-functionalities of the different systems, provides more
-flexibility and less boilerplate, performs better, and
-eliminates the need to learn radically different APIs for
-each event subsystem. The pre-existing classes
-``MapperExtension``, ``SessionExtension``,
-``AttributeExtension``, ``ConnectionProxy``,
-``PoolListener`` as well as the ``DDLElement.execute_at``
-method are deprecated and now implemented in terms of the
-new system - these APIs remain fully functional and are
-expected to remain in place for the foreseeable future.
-
-The new approach uses named events and user-defined
-callables to associate activities with events. The API's
-look and feel was driven by such diverse sources as JQuery,
-Blinker, and Hibernate, and was also modified further on
-several occasions during conferences with dozens of users on
-Twitter, which appears to have a much higher response rate
-than the mailing list for such questions.
-
-It also features an open-ended system of target
-specification that allows events to be associated with API
-classes, such as for all ``Session`` or ``Engine`` objects,
-with specific instances of API classes, such as for a
-specific ``Pool`` or ``Mapper``, as well as for related
-objects like a user- defined class that's mapped, or
-something as specific as a certain attribute on instances of
-a particular subclass of a mapped parent class. Individual
-listener subsystems can apply wrappers to incoming user-
-defined listener functions which modify how they are called
-- an mapper event can receive either the instance of the
-object being operated upon, or its underlying
-``InstanceState`` object. An attribute event can opt whether
-or not to have the responsibility of returning a new value.
-
-Several systems now build upon the new event API, including
-the new "mutable attributes" API as well as composite
-attributes. The greater emphasis on events has also led to
-the introduction of a handful of new events, including
-attribute expiration and refresh operations, pickle
-loads/dumps operations, completed mapper construction
-operations.
-
-.. seealso::
-
- :ref:`event_toplevel`
-
-:ticket:`1902`
-
-Hybrid Attributes, implements/supersedes synonym(), comparable_property()
--------------------------------------------------------------------------
-
-The "derived attributes" example has now been turned into an
-official extension. The typical use case for ``synonym()``
-is to provide descriptor access to a mapped column; the use
-case for ``comparable_property()`` is to be able to return a
-``PropComparator`` from any descriptor. In practice, the
-approach of "derived" is easier to use, more extensible, is
-implemented in a few dozen lines of pure Python with almost
-no imports, and doesn't require the ORM core to even be
-aware of it. The feature is now known as the "Hybrid
-Attributes" extension.
-
-``synonym()`` and ``comparable_property()`` are still part
-of the ORM, though their implementations have been moved
-outwards, building on an approach that is similar to that of
-the hybrid extension, so that the core ORM
-mapper/query/property modules aren't really aware of them
-otherwise.
-
-.. seealso::
-
- :ref:`hybrids_toplevel`
-
-:ticket:`1903`
-
-Speed Enhancements
-------------------
-
-As is customary with all major SQLA releases, a wide pass
-through the internals to reduce overhead and callcounts has
-been made which further reduces the work needed in common
-scenarios. Highlights of this release include:
-
-* The flush process will now bundle INSERT statements into
- batches fed to ``cursor.executemany()``, for rows where
- the primary key is already present. In particular this
- usually applies to the "child" table on a joined table
- inheritance configuration, meaning the number of calls to
- ``cursor.execute`` for a large bulk insert of joined-
- table objects can be cut in half, allowing native DBAPI
- optimizations to take place for those statements passed
- to ``cursor.executemany()`` (such as re-using a prepared
- statement).
-
-* The codepath invoked when accessing a many-to-one
- reference to a related object that's already loaded has
- been greatly simplified. The identity map is checked
- directly without the need to generate a new ``Query``
- object first, which is expensive in the context of
- thousands of in-memory many-to-ones being accessed. The
- usage of constructed-per-call "loader" objects is also no
- longer used for the majority of lazy attribute loads.
-
-* The rewrite of composites allows a shorter codepath when
- mapper internals access mapped attributes within a
- flush.
-
-* New inlined attribute access functions replace the
- previous usage of "history" when the "save-update" and
- other cascade operations need to cascade among the full
- scope of datamembers associated with an attribute. This
- reduces the overhead of generating a new ``History``
- object for this speed-critical operation.
-
-* The internals of the ``ExecutionContext``, the object
- corresponding to a statement execution, have been
- inlined and simplified.
-
-* The ``bind_processor()`` and ``result_processor()``
- callables generated by types for each statement
- execution are now cached (carefully, so as to avoid memory
- leaks for ad-hoc types and dialects) for the lifespan of
- that type, further reducing per-statement call overhead.
-
-* The collection of "bind processors" for a particular
- ``Compiled`` instance of a statement is also cached on
- the ``Compiled`` object, taking further advantage of the
- "compiled cache" used by the flush process to re-use the
- same compiled form of INSERT, UPDATE, DELETE statements.
-
-A demonstration of callcount reduction including a sample
-benchmark script is at
-https://techspot.zzzeek.org/2010/12/12/a-tale-of-three-
-profiles/
-
-Composites Rewritten
---------------------
-
-The "composite" feature has been rewritten, like
-``synonym()`` and ``comparable_property()``, to use a
-lighter weight implementation based on descriptors and
-events, rather than building into the ORM internals. This
-allowed the removal of some latency from the mapper/unit of
-work internals, and simplifies the workings of composite.
-The composite attribute now no longer conceals the
-underlying columns it builds upon, which now remain as
-regular attributes. Composites can also act as a proxy for
-``relationship()`` as well as ``Column()`` attributes.
-
-The major backwards-incompatible change of composites is
-that they no longer use the ``mutable=True`` system to
-detect in-place mutations. Please use the `Mutation
-Tracking <https://www.sqlalchemy.org/docs/07/orm/extensions/m
-utable.html>`_ extension to establish in-place change events
-to existing composite usage.
-
-.. seealso::
-
- :ref:`mapper_composite`
-
- :ref:`mutable_toplevel`
-
-:ticket:`2008` :ticket:`2024`
-
-More succinct form of query.join(target, onclause)
---------------------------------------------------
-
-The default method of issuing ``query.join()`` to a target
-with an explicit onclause is now:
-
-::
-
- query.join(SomeClass, SomeClass.id == ParentClass.some_id)
-
-In 0.6, this usage was considered to be an error, because
-``join()`` accepts multiple arguments corresponding to
-multiple JOIN clauses - the two-argument form needed to be
-in a tuple to disambiguate between single-argument and two-
-argument join targets. In the middle of 0.6 we added
-detection and an error message for this specific calling
-style, since it was so common. In 0.7, since we are
-detecting the exact pattern anyway, and since having to type
-out a tuple for no reason is extremely annoying, the non-
-tuple method now becomes the "normal" way to do it. The
-"multiple JOIN" use case is exceedingly rare compared to the
-single join case, and multiple joins these days are more
-clearly represented by multiple calls to ``join()``.
-
-The tuple form will remain for backwards compatibility.
-
-Note that all the other forms of ``query.join()`` remain
-unchanged:
-
-::
-
- query.join(MyClass.somerelation)
- query.join("somerelation")
- query.join(MyTarget)
- # ... etc
-
-`Querying with Joins
-<https://www.sqlalchemy.org/docs/07/orm/tutorial.html
-#querying-with-joins>`_
-
-:ticket:`1923`
-
-.. _07_migration_mutation_extension:
-
-Mutation event extension, supersedes "mutable=True"
----------------------------------------------------
-
-A new extension, :ref:`mutable_toplevel`, provides a
-mechanism by which user-defined datatypes can provide change
-events back to the owning parent or parents. The extension
-includes an approach for scalar database values, such as
-those managed by :class:`.PickleType`, ``postgresql.ARRAY``, or
-other custom ``MutableType`` classes, as well as an approach
-for ORM "composites", those configured using :func:`~.sqlalchemy.orm.composite`.
-
-.. seealso::
-
- :ref:`mutable_toplevel`
-
-NULLS FIRST / NULLS LAST operators
-----------------------------------
-
-These are implemented as an extension to the ``asc()`` and
-``desc()`` operators, called ``nullsfirst()`` and
-``nullslast()``.
-
-.. seealso::
-
- :func:`.nullsfirst`
-
- :func:`.nullslast`
-
-:ticket:`723`
-
-select.distinct(), query.distinct() accepts \*args for PostgreSQL DISTINCT ON
------------------------------------------------------------------------------
-
-This was already available by passing a list of expressions
-to the ``distinct`` keyword argument of ``select()``, the
-``distinct()`` method of ``select()`` and ``Query`` now
-accept positional arguments which are rendered as DISTINCT
-ON when a PostgreSQL backend is used.
-
-`distinct() <https://www.sqlalchemy.org/docs/07/core/expressi
-on_api.html#sqlalchemy.sql.expression.Select.distinct>`_
-
-`Query.distinct() <https://www.sqlalchemy.org/docs/07/orm/que
-ry.html#sqlalchemy.orm.query.Query.distinct>`_
-
-:ticket:`1069`
-
-``Index()`` can be placed inline inside of ``Table``, ``__table_args__``
-------------------------------------------------------------------------
-
-The Index() construct can be created inline with a Table
-definition, using strings as column names, as an alternative
-to the creation of the index outside of the Table. That is:
-
-::
-
- Table(
- "mytable",
- metadata,
- Column("id", Integer, primary_key=True),
- Column("name", String(50), nullable=False),
- Index("idx_name", "name"),
- )
-
-The primary rationale here is for the benefit of declarative
-``__table_args__``, particularly when used with mixins:
-
-::
-
- class HasNameMixin(object):
- name = Column("name", String(50), nullable=False)
-
- @declared_attr
- def __table_args__(cls):
- return (Index("name"), {})
-
-
- class User(HasNameMixin, Base):
- __tablename__ = "user"
- id = Column("id", Integer, primary_key=True)
-
-`Indexes <https://www.sqlalchemy.org/docs/07/core/schema.html
-#indexes>`_
-
-Window Function SQL Construct
------------------------------
-
-A "window function" provides to a statement information
-about the result set as it's produced. This allows criteria
-against various things like "row number", "rank" and so
-forth. They are known to be supported at least by
-PostgreSQL, SQL Server and Oracle, possibly others.
-
-The best introduction to window functions is on PostgreSQL's
-site, where window functions have been supported since
-version 8.4:
-
-https://www.postgresql.org/docs/current/static/tutorial-window.html
-
-SQLAlchemy provides a simple construct typically invoked via
-an existing function clause, using the ``over()`` method,
-which accepts ``order_by`` and ``partition_by`` keyword
-arguments. Below we replicate the first example in PG's
-tutorial:
-
-::
-
- from sqlalchemy.sql import table, column, select, func
-
- empsalary = table("empsalary", column("depname"), column("empno"), column("salary"))
-
- s = select(
- [
- empsalary,
- func.avg(empsalary.c.salary)
- .over(partition_by=empsalary.c.depname)
- .label("avg"),
- ]
- )
-
- print(s)
-
-SQL:
-
-.. sourcecode:: sql
-
- SELECT empsalary.depname, empsalary.empno, empsalary.salary,
- avg(empsalary.salary) OVER (PARTITION BY empsalary.depname) AS avg
- FROM empsalary
-
-`sqlalchemy.sql.expression.over <https://www.sqlalchemy.org/d
-ocs/07/core/expression_api.html#sqlalchemy.sql.expression.ov
-er>`_
-
-:ticket:`1844`
-
-execution_options() on Connection accepts "isolation_level" argument
---------------------------------------------------------------------
-
-This sets the transaction isolation level for a single
-``Connection``, until that ``Connection`` is closed and its
-underlying DBAPI resource returned to the connection pool,
-upon which the isolation level is reset back to the default.
-The default isolation level is set using the
-``isolation_level`` argument to ``create_engine()``.
-
-Transaction isolation support is currently only supported by
-the PostgreSQL and SQLite backends.
-
-`execution_options() <https://www.sqlalchemy.org/docs/07/core
-/connections.html#sqlalchemy.engine.base.Connection.executio
-n_options>`_
-
-:ticket:`2001`
-
-``TypeDecorator`` works with integer primary key columns
---------------------------------------------------------
-
-A ``TypeDecorator`` which extends the behavior of
-``Integer`` can be used with a primary key column. The
-"autoincrement" feature of ``Column`` will now recognize
-that the underlying database column is still an integer so
-that lastrowid mechanisms continue to function. The
-``TypeDecorator`` itself will have its result value
-processor applied to newly generated primary keys, including
-those received by the DBAPI ``cursor.lastrowid`` accessor.
-
-:ticket:`2005` :ticket:`2006`
-
-``TypeDecorator`` is present in the "sqlalchemy" import space
--------------------------------------------------------------
-
-No longer need to import this from ``sqlalchemy.types``,
-it's now mirrored in ``sqlalchemy``.
-
-New Dialects
-------------
-
-Dialects have been added:
-
-* a MySQLdb driver for the Drizzle database:
-
-
- `Drizzle <https://www.sqlalchemy.org/docs/07/dialects/drizz
- le.html>`_
-
-* support for the pymysql DBAPI:
-
-
- `pymsql Notes
- <https://www.sqlalchemy.org/docs/07/dialects/mysql.html
- #module-sqlalchemy.dialects.mysql.pymysql>`_
-
-* psycopg2 now works with Python 3
-
-
-Behavioral Changes (Backwards Compatible)
-=========================================
-
-C Extensions Build by Default
------------------------------
-
-This is as of 0.7b4. The exts will build if cPython 2.xx
-is detected. If the build fails, such as on a windows
-install, that condition is caught and the non-C install
-proceeds. The C exts won't build if Python 3 or PyPy is
-used.
-
-Query.count() simplified, should work virtually always
-------------------------------------------------------
-
-The very old guesswork which occurred within
-``Query.count()`` has been modernized to use
-``.from_self()``. That is, ``query.count()`` is now
-equivalent to:
-
-::
-
- query.from_self(func.count(literal_column("1"))).scalar()
-
-Previously, internal logic attempted to rewrite the columns
-clause of the query itself, and upon detection of a
-"subquery" condition, such as a column-based query that
-might have aggregates in it, or a query with DISTINCT, would
-go through a convoluted process of rewriting the columns
-clause. This logic failed in complex conditions,
-particularly those involving joined table inheritance, and
-was long obsolete by the more comprehensive ``.from_self()``
-call.
-
-The SQL emitted by ``query.count()`` is now always of the
-form:
-
-.. sourcecode:: sql
-
- SELECT count(1) AS count_1 FROM (
- SELECT user.id AS user_id, user.name AS user_name from user
- ) AS anon_1
-
-that is, the original query is preserved entirely inside of
-a subquery, with no more guessing as to how count should be
-applied.
-
-:ticket:`2093`
-
-To emit a non-subquery form of count()
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-MySQL users have already reported that the MyISAM engine not
-surprisingly falls over completely with this simple change.
-Note that for a simple ``count()`` that optimizes for DBs
-that can't handle simple subqueries, ``func.count()`` should
-be used:
-
-::
-
- from sqlalchemy import func
-
- session.query(func.count(MyClass.id)).scalar()
-
-or for ``count(*)``:
-
-::
-
- from sqlalchemy import func, literal_column
-
- session.query(func.count(literal_column("*"))).select_from(MyClass).scalar()
-
-LIMIT/OFFSET clauses now use bind parameters
---------------------------------------------
-
-The LIMIT and OFFSET clauses, or their backend equivalents
-(i.e. TOP, ROW NUMBER OVER, etc.), use bind parameters for
-the actual values, for all backends which support it (most
-except for Sybase). This allows better query optimizer
-performance as the textual string for multiple statements
-with differing LIMIT/OFFSET are now identical.
-
-:ticket:`805`
-
-Logging enhancements
---------------------
-
-Vinay Sajip has provided a patch to our logging system such
-that the "hex string" embedded in logging statements for
-engines and pools is no longer needed to allow the ``echo``
-flag to work correctly. A new system that uses filtered
-logging objects allows us to maintain our current behavior
-of ``echo`` being local to individual engines without the
-need for additional identifying strings local to those
-engines.
-
-:ticket:`1926`
-
-Simplified polymorphic_on assignment
-------------------------------------
-
-The population of the ``polymorphic_on`` column-mapped
-attribute, when used in an inheritance scenario, now occurs
-when the object is constructed, i.e. its ``__init__`` method
-is called, using the init event. The attribute then behaves
-the same as any other column-mapped attribute. Previously,
-special logic would fire off during flush to populate this
-column, which prevented any user code from modifying its
-behavior. The new approach improves upon this in three
-ways: 1. the polymorphic identity is now present on the
-object as soon as its constructed; 2. the polymorphic
-identity can be changed by user code without any difference
-in behavior from any other column-mapped attribute; 3. the
-internals of the mapper during flush are simplified and no
-longer need to make special checks for this column.
-
-:ticket:`1895`
-
-contains_eager() chains across multiple paths (i.e. "all()")
-------------------------------------------------------------
-
-The ```contains_eager()```` modifier now will chain itself
-for a longer path without the need to emit individual
-````contains_eager()``` calls. Instead of:
-
-::
-
- session.query(A).options(contains_eager(A.b), contains_eager(A.b, B.c))
-
-you can say:
-
-::
-
- session.query(A).options(contains_eager(A.b, B.c))
-
-:ticket:`2032`
-
-Flushing of orphans that have no parent is allowed
---------------------------------------------------
-
-We've had a long standing behavior that checks for a so-
-called "orphan" during flush, that is, an object which is
-associated with a ``relationship()`` that specifies "delete-
-orphan" cascade, has been newly added to the session for an
-INSERT, and no parent relationship has been established.
-This check was added years ago to accommodate some test
-cases which tested the orphan behavior for consistency. In
-modern SQLA, this check is no longer needed on the Python
-side. The equivalent behavior of the "orphan check" is
-accomplished by making the foreign key reference to the
-object's parent row NOT NULL, where the database does its
-job of establishing data consistency in the same way SQLA
-allows most other operations to do. If the object's parent
-foreign key is nullable, then the row can be inserted. The
-"orphan" behavior runs when the object was persisted with a
-particular parent, and is then disassociated with that
-parent, leading to a DELETE statement emitted for it.
-
-:ticket:`1912`
-
-Warnings generated when collection members, scalar referents not part of the flush
-----------------------------------------------------------------------------------
-
-Warnings are now emitted when related objects referenced via
-a loaded ``relationship()`` on a parent object marked as
-"dirty" are not present in the current ``Session``.
-
-The ``save-update`` cascade takes effect when objects are
-added to the ``Session``, or when objects are first
-associated with a parent, so that an object and everything
-related to it are usually all present in the same
-``Session``. However, if ``save-update`` cascade is
-disabled for a particular ``relationship()``, then this
-behavior does not occur, and the flush process does not try
-to correct for it, instead staying consistent to the
-configured cascade behavior. Previously, when such objects
-were detected during the flush, they were silently skipped.
-The new behavior is that a warning is emitted, for the
-purposes of alerting to a situation that more often than not
-is the source of unexpected behavior.
-
-:ticket:`1973`
-
-Setup no longer installs a Nose plugin
---------------------------------------
-
-Since we moved to nose we've used a plugin that installs via
-setuptools, so that the ``nosetests`` script would
-automatically run SQLA's plugin code, necessary for our
-tests to have a full environment. In the middle of 0.6, we
-realized that the import pattern here meant that Nose's
-"coverage" plugin would break, since "coverage" requires
-that it be started before any modules to be covered are
-imported; so in the middle of 0.6 we made the situation
-worse by adding a separate ``sqlalchemy-nose`` package to
-the build to overcome this.
-
-In 0.7 we've done away with trying to get ``nosetests`` to
-work automatically, since the SQLAlchemy module would
-produce a large number of nose configuration options for all
-usages of ``nosetests``, not just the SQLAlchemy unit tests
-themselves, and the additional ``sqlalchemy-nose`` install
-was an even worse idea, producing an extra package in Python
-environments. The ``sqla_nose.py`` script in 0.7 is now
-the only way to run the tests with nose.
-
-:ticket:`1949`
-
-Non-``Table``-derived constructs can be mapped
-----------------------------------------------
-
-A construct that isn't against any ``Table`` at all, like a
-function, can be mapped.
-
-::
-
- from sqlalchemy import select, func
- from sqlalchemy.orm import mapper
-
-
- class Subset(object):
- pass
-
-
- selectable = select(["x", "y", "z"]).select_from(func.some_db_function()).alias()
- mapper(Subset, selectable, primary_key=[selectable.c.x])
-
-:ticket:`1876`
-
-aliased() accepts ``FromClause`` elements
------------------------------------------
-
-This is a convenience helper such that in the case a plain
-``FromClause``, such as a ``select``, ``Table`` or ``join``
-is passed to the ``orm.aliased()`` construct, it passes
-through to the ``.alias()`` method of that from construct
-rather than constructing an ORM level ``AliasedClass``.
-
-:ticket:`2018`
-
-Session.connection(), Session.execute() accept 'bind'
------------------------------------------------------
-
-This is to allow execute/connection operations to
-participate in the open transaction of an engine explicitly.
-It also allows custom subclasses of ``Session`` that
-implement their own ``get_bind()`` method and arguments to
-use those custom arguments with both the ``execute()`` and
-``connection()`` methods equally.
-
-`Session.connection <https://www.sqlalchemy.org/docs/07/orm/s
-ession.html#sqlalchemy.orm.session.Session.connection>`_
-`Session.execute <https://www.sqlalchemy.org/docs/07/orm/sess
-ion.html#sqlalchemy.orm.session.Session.execute>`_
-
-:ticket:`1996`
-
-Standalone bind parameters in columns clause auto-labeled.
-----------------------------------------------------------
-
-Bind parameters present in the "columns clause" of a select
-are now auto-labeled like other "anonymous" clauses, which
-among other things allows their "type" to be meaningful when
-the row is fetched, as in result row processors.
-
-SQLite - relative file paths are normalized through os.path.abspath()
----------------------------------------------------------------------
-
-This so that a script that changes the current directory
-will continue to target the same location as subsequent
-SQLite connections are established.
-
-:ticket:`2036`
-
-MS-SQL - ``String``/``Unicode``/``VARCHAR``/``NVARCHAR``/``VARBINARY`` emit "max" for no length
------------------------------------------------------------------------------------------------
-
-On the MS-SQL backend, the String/Unicode types, and their
-counterparts VARCHAR/ NVARCHAR, as well as VARBINARY
-(:ticket:`1833`) emit "max" as the length when no length is
-specified. This makes it more compatible with PostgreSQL's
-VARCHAR type which is similarly unbounded when no length
-specified. SQL Server defaults the length on these types
-to '1' when no length is specified.
-
-Behavioral Changes (Backwards Incompatible)
-===========================================
-
-Note again, aside from the default mutability change, most
-of these changes are \*extremely minor* and will not affect
-most users.
-
-``PickleType`` and ARRAY mutability turned off by default
----------------------------------------------------------
-
-This change refers to the default behavior of the ORM when
-mapping columns that have either the ``PickleType`` or
-``postgresql.ARRAY`` datatypes. The ``mutable`` flag is now
-set to ``False`` by default. If an existing application uses
-these types and depends upon detection of in-place
-mutations, the type object must be constructed with
-``mutable=True`` to restore the 0.6 behavior:
-
-::
-
- Table(
- "mytable",
- metadata,
- # ....
- Column("pickled_data", PickleType(mutable=True)),
- )
-
-The ``mutable=True`` flag is being phased out, in favor of
-the new `Mutation Tracking <https://www.sqlalchemy.org/docs/0
-7/orm/extensions/mutable.html>`_ extension. This extension
-provides a mechanism by which user-defined datatypes can
-provide change events back to the owning parent or parents.
-
-The previous approach of using ``mutable=True`` does not
-provide for change events - instead, the ORM must scan
-through all mutable values present in a session and compare
-them against their original value for changes every time
-``flush()`` is called, which is a very time consuming event.
-This is a holdover from the very early days of SQLAlchemy
-when ``flush()`` was not automatic and the history tracking
-system was not nearly as sophisticated as it is now.
-
-Existing applications which use ``PickleType``,
-``postgresql.ARRAY`` or other ``MutableType`` subclasses,
-and require in-place mutation detection, should migrate to
-the new mutation tracking system, as ``mutable=True`` is
-likely to be deprecated in the future.
-
-:ticket:`1980`
-
-Mutability detection of ``composite()`` requires the Mutation Tracking Extension
---------------------------------------------------------------------------------
-
-So-called "composite" mapped attributes, those configured
-using the technique described at `Composite Column Types
-<https://www.sqlalchemy.org/docs/07/orm/mapper_config.html
-#composite-column-types>`_, have been re-implemented such
-that the ORM internals are no longer aware of them (leading
-to shorter and more efficient codepaths in critical
-sections). While composite types are generally intended to
-be treated as immutable value objects, this was never
-enforced. For applications that use composites with
-mutability, the `Mutation Tracking <https://www.sqlalchemy.or
-g/docs/07/orm/extensions/mutable.html>`_ extension offers a
-base class which establishes a mechanism for user-defined
-composite types to send change event messages back to the
-owning parent or parents of each object.
-
-Applications which use composite types and rely upon in-
-place mutation detection of these objects should either
-migrate to the "mutation tracking" extension, or change the
-usage of the composite types such that in-place changes are
-no longer needed (i.e., treat them as immutable value
-objects).
-
-SQLite - the SQLite dialect now uses ``NullPool`` for file-based databases
---------------------------------------------------------------------------
-
-This change is **99.999% backwards compatible**, unless you
-are using temporary tables across connection pool
-connections.
-
-A file-based SQLite connection is blazingly fast, and using
-``NullPool`` means that each call to ``Engine.connect``
-creates a new pysqlite connection.
-
-Previously, the ``SingletonThreadPool`` was used, which
-meant that all connections to a certain engine in a thread
-would be the same connection. It's intended that the new
-approach is more intuitive, particularly when multiple
-connections are used.
-
-``SingletonThreadPool`` is still the default engine when a
-``:memory:`` database is used.
-
-Note that this change **breaks temporary tables used across
-Session commits**, due to the way SQLite handles temp
-tables. See the note at
-https://www.sqlalchemy.org/docs/dialects/sqlite.html#using-
-temporary-tables-with-sqlite if temporary tables beyond the
-scope of one pool connection are desired.
-
-:ticket:`1921`
-
-``Session.merge()`` checks version ids for versioned mappers
-------------------------------------------------------------
-
-Session.merge() will check the version id of the incoming
-state against that of the database, assuming the mapping
-uses version ids and incoming state has a version_id
-assigned, and raise StaleDataError if they don't match.
-This is the correct behavior, in that if incoming state
-contains a stale version id, it should be assumed the state
-is stale.
-
-If merging data into a versioned state, the version id
-attribute can be left undefined, and no version check will
-take place.
-
-This check was confirmed by examining what Hibernate does -
-both the ``merge()`` and the versioning features were
-originally adapted from Hibernate.
-
-:ticket:`2027`
-
-Tuple label names in Query Improved
------------------------------------
-
-This improvement is potentially slightly backwards
-incompatible for an application that relied upon the old
-behavior.
-
-Given two mapped classes ``Foo`` and ``Bar`` each with a
-column ``spam``:
-
-::
-
-
- qa = session.query(Foo.spam)
- qb = session.query(Bar.spam)
-
- qu = qa.union(qb)
-
-The name given to the single column yielded by ``qu`` will
-be ``spam``. Previously it would be something like
-``foo_spam`` due to the way the ``union`` would combine
-things, which is inconsistent with the name ``spam`` in the
-case of a non-unioned query.
-
-:ticket:`1942`
-
-Mapped column attributes reference the most specific column first
------------------------------------------------------------------
-
-This is a change to the behavior involved when a mapped
-column attribute references multiple columns, specifically
-when dealing with an attribute on a joined-table subclass
-that has the same name as that of an attribute on the
-superclass.
-
-Using declarative, the scenario is this:
-
-::
-
- class Parent(Base):
- __tablename__ = "parent"
- id = Column(Integer, primary_key=True)
-
-
- class Child(Parent):
- __tablename__ = "child"
- id = Column(Integer, ForeignKey("parent.id"), primary_key=True)
-
-Above, the attribute ``Child.id`` refers to both the
-``child.id`` column as well as ``parent.id`` - this due to
-the name of the attribute. If it were named differently on
-the class, such as ``Child.child_id``, it then maps
-distinctly to ``child.id``, with ``Child.id`` being the same
-attribute as ``Parent.id``.
-
-When the ``id`` attribute is made to reference both
-``parent.id`` and ``child.id``, it stores them in an ordered
-list. An expression such as ``Child.id`` then refers to
-just *one* of those columns when rendered. Up until 0.6,
-this column would be ``parent.id``. In 0.7, it is the less
-surprising ``child.id``.
-
-The legacy of this behavior deals with behaviors and
-restrictions of the ORM that don't really apply anymore; all
-that was needed was to reverse the order.
-
-A primary advantage of this approach is that it's now easier
-to construct ``primaryjoin`` expressions that refer to the
-local column:
-
-::
-
- class Child(Parent):
- __tablename__ = "child"
- id = Column(Integer, ForeignKey("parent.id"), primary_key=True)
- some_related = relationship(
- "SomeRelated", primaryjoin="Child.id==SomeRelated.child_id"
- )
-
-
- class SomeRelated(Base):
- __tablename__ = "some_related"
- id = Column(Integer, primary_key=True)
- child_id = Column(Integer, ForeignKey("child.id"))
-
-Prior to 0.7 the ``Child.id`` expression would reference
-``Parent.id``, and it would be necessary to map ``child.id``
-to a distinct attribute.
-
-It also means that a query like this one changes its
-behavior:
-
-::
-
- session.query(Parent).filter(Child.id > 7)
-
-In 0.6, this would render:
-
-.. sourcecode:: sql
-
- SELECT parent.id AS parent_id
- FROM parent
- WHERE parent.id > :id_1
-
-in 0.7, you get:
-
-.. sourcecode:: sql
-
- SELECT parent.id AS parent_id
- FROM parent, child
- WHERE child.id > :id_1
-
-which you'll note is a cartesian product - this behavior is
-now equivalent to that of any other attribute that is local
-to ``Child``. The ``with_polymorphic()`` method, or a
-similar strategy of explicitly joining the underlying
-``Table`` objects, is used to render a query against all
-``Parent`` objects with criteria against ``Child``, in the
-same manner as that of 0.5 and 0.6:
-
-::
-
- print(s.query(Parent).with_polymorphic([Child]).filter(Child.id > 7))
-
-Which on both 0.6 and 0.7 renders:
-
-.. sourcecode:: sql
-
- SELECT parent.id AS parent_id, child.id AS child_id
- FROM parent LEFT OUTER JOIN child ON parent.id = child.id
- WHERE child.id > :id_1
-
-Another effect of this change is that a joined-inheritance
-load across two tables will populate from the child table's
-value, not that of the parent table. An unusual case is that
-a query against "Parent" using ``with_polymorphic="*"``
-issues a query against "parent", with a LEFT OUTER JOIN to
-"child". The row is located in "Parent", sees the
-polymorphic identity corresponds to "Child", but suppose the
-actual row in "child" has been *deleted*. Due to this
-corruption, the row comes in with all the columns
-corresponding to "child" set to NULL - this is now the value
-that gets populated, not the one in the parent table.
-
-:ticket:`1892`
-
-Mapping to joins with two or more same-named columns requires explicit declaration
-----------------------------------------------------------------------------------
-
-This is somewhat related to the previous change in
-:ticket:`1892`. When mapping to a join, same-named columns
-must be explicitly linked to mapped attributes, i.e. as
-described in `Mapping a Class Against Multiple Tables <http:
-//www.sqlalchemy.org/docs/07/orm/mapper_config.html#mapping-
-a-class-against-multiple-tables>`_.
-
-Given two tables ``foo`` and ``bar``, each with a primary
-key column ``id``, the following now produces an error:
-
-::
-
-
- foobar = foo.join(bar, foo.c.id == bar.c.foo_id)
- mapper(FooBar, foobar)
-
-This because the ``mapper()`` refuses to guess what column
-is the primary representation of ``FooBar.id`` - is it
-``foo.c.id`` or is it ``bar.c.id`` ? The attribute must be
-explicit:
-
-::
-
-
- foobar = foo.join(bar, foo.c.id == bar.c.foo_id)
- mapper(FooBar, foobar, properties={"id": [foo.c.id, bar.c.id]})
-
-:ticket:`1896`
-
-Mapper requires that polymorphic_on column be present in the mapped selectable
-------------------------------------------------------------------------------
-
-This is a warning in 0.6, now an error in 0.7. The column
-given for ``polymorphic_on`` must be in the mapped
-selectable. This to prevent some occasional user errors
-such as:
-
-::
-
- mapper(SomeClass, sometable, polymorphic_on=some_lookup_table.c.id)
-
-where above the polymorphic_on needs to be on a
-``sometable`` column, in this case perhaps
-``sometable.c.some_lookup_id``. There are also some
-"polymorphic union" scenarios where similar mistakes
-sometimes occur.
-
-Such a configuration error has always been "wrong", and the
-above mapping doesn't work as specified - the column would
-be ignored. It is however potentially backwards
-incompatible in the rare case that an application has been
-unknowingly relying upon this behavior.
-
-:ticket:`1875`
-
-``DDL()`` constructs now escape percent signs
----------------------------------------------
-
-Previously, percent signs in ``DDL()`` strings would have to
-be escaped, i.e. ``%%`` depending on DBAPI, for those DBAPIs
-that accept ``pyformat`` or ``format`` binds (i.e. psycopg2,
-mysql-python), which was inconsistent versus ``text()``
-constructs which did this automatically. The same escaping
-now occurs for ``DDL()`` as for ``text()``.
-
-:ticket:`1897`
-
-``Table.c`` / ``MetaData.tables`` refined a bit, don't allow direct mutation
-----------------------------------------------------------------------------
-
-Another area where some users were tinkering around in such
-a way that doesn't actually work as expected, but still left
-an exceedingly small chance that some application was
-relying upon this behavior, the construct returned by the
-``.c`` attribute on ``Table`` and the ``.tables`` attribute
-on ``MetaData`` is explicitly non-mutable. The "mutable"
-version of the construct is now private. Adding columns to
-``.c`` involves using the ``append_column()`` method of
-``Table``, which ensures things are associated with the
-parent ``Table`` in the appropriate way; similarly,
-``MetaData.tables`` has a contract with the ``Table``
-objects stored in this dictionary, as well as a little bit
-of new bookkeeping in that a ``set()`` of all schema names
-is tracked, which is satisfied only by using the public
-``Table`` constructor as well as ``Table.tometadata()``.
-
-It is of course possible that the ``ColumnCollection`` and
-``dict`` collections consulted by these attributes could
-someday implement events on all of their mutational methods
-such that the appropriate bookkeeping occurred upon direct
-mutation of the collections, but until someone has the
-motivation to implement all that along with dozens of new
-unit tests, narrowing the paths to mutation of these
-collections will ensure no application is attempting to rely
-upon usages that are currently not supported.
-
-:ticket:`1893` :ticket:`1917`
-
-server_default consistently returns None for all inserted_primary_key values
-----------------------------------------------------------------------------
-
-Established consistency when server_default is present on an
-Integer PK column. SQLA doesn't pre-fetch these, nor do they
-come back in cursor.lastrowid (DBAPI). Ensured all backends
-consistently return None in result.inserted_primary_key for
-these - some backends may have returned a value previously.
-Using a server_default on a primary key column is extremely
-unusual. If a special function or SQL expression is used
-to generate primary key defaults, this should be established
-as a Python-side "default" instead of server_default.
-
-Regarding reflection for this case, reflection of an int PK
-col with a server_default sets the "autoincrement" flag to
-False, except in the case of a PG SERIAL col where we
-detected a sequence default.
-
-:ticket:`2020` :ticket:`2021`
-
-The ``sqlalchemy.exceptions`` alias in sys.modules is removed
--------------------------------------------------------------
-
-For a few years we've added the string
-``sqlalchemy.exceptions`` to ``sys.modules``, so that a
-statement like "``import sqlalchemy.exceptions``" would
-work. The name of the core exceptions module has been
-``exc`` for a long time now, so the recommended import for
-this module is:
-
-::
-
- from sqlalchemy import exc
-
-The ``exceptions`` name is still present in "``sqlalchemy``"
-for applications which might have said ``from sqlalchemy
-import exceptions``, but they should also start using the
-``exc`` name.
-
-Query Timing Recipe Changes
----------------------------
-
-While not part of SQLAlchemy itself, it's worth mentioning
-that the rework of the ``ConnectionProxy`` into the new
-event system means it is no longer appropriate for the
-"Timing all Queries" recipe. Please adjust query-timers to
-use the ``before_cursor_execute()`` and
-``after_cursor_execute()`` events, demonstrated in the
-updated recipe UsageRecipes/Profiling.
-
-Deprecated API
-==============
-
-Default constructor on types will not accept arguments
-------------------------------------------------------
-
-Simple types like ``Integer``, ``Date`` etc. in the core
-types module don't accept arguments. The default
-constructor that accepts/ignores a catchall ``\*args,
-\**kwargs`` is restored as of 0.7b4/0.7.0, but emits a
-deprecation warning.
-
-If arguments are being used with a core type like
-``Integer``, it may be that you intended to use a dialect
-specific type, such as ``sqlalchemy.dialects.mysql.INTEGER``
-which does accept a "display_width" argument for example.
-
-compile_mappers() renamed configure_mappers(), simplified configuration internals
----------------------------------------------------------------------------------
-
-This system slowly morphed from something small, implemented
-local to an individual mapper, and poorly named into
-something that's more of a global "registry-" level function
-and poorly named, so we've fixed both by moving the
-implementation out of ``Mapper`` altogether and renaming it
-to ``configure_mappers()``. It is of course normally not
-needed for an application to call ``configure_mappers()`` as
-this process occurs on an as-needed basis, as soon as the
-mappings are needed via attribute or query access.
-
-:ticket:`1966`
-
-Core listener/proxy superseded by event listeners
--------------------------------------------------
-
-``PoolListener``, ``ConnectionProxy``,
-``DDLElement.execute_at`` are superseded by
-``event.listen()``, using the ``PoolEvents``,
-``EngineEvents``, ``DDLEvents`` dispatch targets,
-respectively.
-
-ORM extensions superseded by event listeners
---------------------------------------------
-
-``MapperExtension``, ``AttributeExtension``,
-``SessionExtension`` are superseded by ``event.listen()``,
-using the ``MapperEvents``/``InstanceEvents``,
-``AttributeEvents``, ``SessionEvents``, dispatch targets,
-respectively.
-
-Sending a string to 'distinct' in select() for MySQL should be done via prefixes
---------------------------------------------------------------------------------
-
-This obscure feature allows this pattern with the MySQL
-backend:
-
-::
-
- select([mytable], distinct="ALL", prefixes=["HIGH_PRIORITY"])
-
-The ``prefixes`` keyword or ``prefix_with()`` method should
-be used for non-standard or unusual prefixes:
-
-::
-
- select([mytable]).prefix_with("HIGH_PRIORITY", "ALL")
-
-``useexisting`` superseded by ``extend_existing`` and ``keep_existing``
------------------------------------------------------------------------
-
-The ``useexisting`` flag on Table has been superseded by a
-new pair of flags ``keep_existing`` and ``extend_existing``.
-``extend_existing`` is equivalent to ``useexisting`` - the
-existing Table is returned, and additional constructor
-elements are added. With ``keep_existing``, the existing
-Table is returned, but additional constructor elements are
-not added - these elements are only applied when the Table
-is newly created.
-
-Backwards Incompatible API Changes
-==================================
-
-Callables passed to ``bindparam()`` don't get evaluated - affects the Beaker example
-------------------------------------------------------------------------------------
-
-:ticket:`1950`
-
-Note this affects the Beaker caching example, where the
-workings of the ``_params_from_query()`` function needed a
-slight adjustment. If you're using code from the Beaker
-example, this change should be applied.
-
-types.type_map is now private, types._type_map
-----------------------------------------------
-
-We noticed some users tapping into this dictionary inside of
-``sqlalchemy.types`` as a shortcut to associating Python
-types with SQL types. We can't guarantee the contents or
-format of this dictionary, and additionally the business of
-associating Python types in a one-to-one fashion has some
-grey areas that should are best decided by individual
-applications, so we've underscored this attribute.
-
-:ticket:`1870`
-
-Renamed the ``alias`` keyword arg of standalone ``alias()`` function to ``name``
---------------------------------------------------------------------------------
-
-This so that the keyword argument ``name`` matches that of
-the ``alias()`` methods on all ``FromClause`` objects as
-well as the ``name`` argument on ``Query.subquery()``.
-
-Only code that uses the standalone ``alias()`` function, and
-not the method bound functions, and passes the alias name
-using the explicit keyword name ``alias``, and not
-positionally, would need modification here.
-
-Non-public ``Pool`` methods underscored
----------------------------------------
-
-All methods of ``Pool`` and subclasses which are not
-intended for public use have been renamed with underscores.
-That they were not named this way previously was a bug.
-
-Pooling methods now underscored or removed:
-
-``Pool.create_connection()`` ->
-``Pool._create_connection()``
-
-``Pool.do_get()`` -> ``Pool._do_get()``
-
-``Pool.do_return_conn()`` -> ``Pool._do_return_conn()``
-
-``Pool.do_return_invalid()`` -> removed, was not used
-
-``Pool.return_conn()`` -> ``Pool._return_conn()``
-
-``Pool.get()`` -> ``Pool._get()``, public API is
-``Pool.connect()``
-
-``SingletonThreadPool.cleanup()`` -> ``_cleanup()``
-
-``SingletonThreadPool.dispose_local()`` -> removed, use
-``conn.invalidate()``
-
-:ticket:`1982`
-
-Previously Deprecated, Now Removed
-==================================
-
-Query.join(), Query.outerjoin(), eagerload(), eagerload_all(), others no longer allow lists of attributes as arguments
-----------------------------------------------------------------------------------------------------------------------
-
-Passing a list of attributes or attribute names to
-``Query.join``, ``eagerload()``, and similar has been
-deprecated since 0.5:
-
-::
-
- # old way, deprecated since 0.5
- session.query(Houses).join([Houses.rooms, Room.closets])
- session.query(Houses).options(eagerload_all([Houses.rooms, Room.closets]))
-
-These methods all accept \*args as of the 0.5 series:
-
-::
-
- # current way, in place since 0.5
- session.query(Houses).join(Houses.rooms, Room.closets)
- session.query(Houses).options(eagerload_all(Houses.rooms, Room.closets))
-
-``ScopedSession.mapper`` is removed
------------------------------------
-
-This feature provided a mapper extension which linked class-
-based functionality with a particular ``ScopedSession``, in
-particular providing the behavior such that new object
-instances would be automatically associated with that
-session. The feature was overused by tutorials and
-frameworks which led to great user confusion due to its
-implicit behavior, and was deprecated in 0.5.5. Techniques
-for replicating its functionality are at
-[wiki:UsageRecipes/SessionAwareMapper]
-
+++ /dev/null
-=============================
-What's New in SQLAlchemy 0.8?
-=============================
-
-.. admonition:: About this Document
-
- This document describes changes between SQLAlchemy version 0.7,
- undergoing maintenance releases as of October, 2012,
- and SQLAlchemy version 0.8, which is expected for release
- in early 2013.
-
- Document date: October 25, 2012
- Updated: March 9, 2013
-
-Introduction
-============
-
-This guide introduces what's new in SQLAlchemy version 0.8,
-and also documents changes which affect users migrating
-their applications from the 0.7 series of SQLAlchemy to 0.8.
-
-SQLAlchemy releases are closing in on 1.0, and each new
-version since 0.5 features fewer major usage changes. Most
-applications that are settled into modern 0.7 patterns
-should be movable to 0.8 with no changes. Applications that
-use 0.6 and even 0.5 patterns should be directly migratable
-to 0.8 as well, though larger applications may want to test
-with each interim version.
-
-Platform Support
-================
-
-Targeting Python 2.5 and Up Now
--------------------------------
-
-SQLAlchemy 0.8 will target Python 2.5 and forward;
-compatibility for Python 2.4 is being dropped.
-
-The internals will be able to make usage of Python ternaries
-(that is, ``x if y else z``) which will improve things
-versus the usage of ``y and x or z``, which naturally has
-been the source of some bugs, as well as context managers
-(that is, ``with:``) and perhaps in some cases
-``try:/except:/else:`` blocks which will help with code
-readability.
-
-SQLAlchemy will eventually drop 2.5 support as well - when
-2.6 is reached as the baseline, SQLAlchemy will move to use
-2.6/3.3 in-place compatibility, removing the usage of the
-``2to3`` tool and maintaining a source base that works with
-Python 2 and 3 at the same time.
-
-New ORM Features
-================
-
-.. _feature_relationship_08:
-
-Rewritten :func:`_orm.relationship` mechanics
-----------------------------------------------
-0.8 features a much improved and capable system regarding
-how :func:`_orm.relationship` determines how to join between two
-entities. The new system includes these features:
-
-* The ``primaryjoin`` argument is **no longer needed** when
- constructing a :func:`_orm.relationship` against a class that
- has multiple foreign key paths to the target. Only the
- ``foreign_keys`` argument is needed to specify those
- columns which should be included:
-
- ::
-
-
- class Parent(Base):
- __tablename__ = "parent"
- id = Column(Integer, primary_key=True)
- child_id_one = Column(Integer, ForeignKey("child.id"))
- child_id_two = Column(Integer, ForeignKey("child.id"))
-
- child_one = relationship("Child", foreign_keys=child_id_one)
- child_two = relationship("Child", foreign_keys=child_id_two)
-
-
- class Child(Base):
- __tablename__ = "child"
- id = Column(Integer, primary_key=True)
-
-* relationships against self-referential, composite foreign
- keys where **a column points to itself** are now
- supported. The canonical case is as follows:
-
- ::
-
- class Folder(Base):
- __tablename__ = "folder"
- __table_args__ = (
- ForeignKeyConstraint(
- ["account_id", "parent_id"], ["folder.account_id", "folder.folder_id"]
- ),
- )
-
- account_id = Column(Integer, primary_key=True)
- folder_id = Column(Integer, primary_key=True)
- parent_id = Column(Integer)
- name = Column(String)
-
- parent_folder = relationship(
- "Folder", backref="child_folders", remote_side=[account_id, folder_id]
- )
-
- Above, the ``Folder`` refers to its parent ``Folder``
- joining from ``account_id`` to itself, and ``parent_id``
- to ``folder_id``. When SQLAlchemy constructs an auto-
- join, no longer can it assume all columns on the "remote"
- side are aliased, and all columns on the "local" side are
- not - the ``account_id`` column is **on both sides**. So
- the internal relationship mechanics were totally rewritten
- to support an entirely different system whereby two copies
- of ``account_id`` are generated, each containing different
- *annotations* to determine their role within the
- statement. Note the join condition within a basic eager
- load:
-
- .. sourcecode:: sql
-
- SELECT
- folder.account_id AS folder_account_id,
- folder.folder_id AS folder_folder_id,
- folder.parent_id AS folder_parent_id,
- folder.name AS folder_name,
- folder_1.account_id AS folder_1_account_id,
- folder_1.folder_id AS folder_1_folder_id,
- folder_1.parent_id AS folder_1_parent_id,
- folder_1.name AS folder_1_name
- FROM folder
- LEFT OUTER JOIN folder AS folder_1
- ON
- folder_1.account_id = folder.account_id
- AND folder.folder_id = folder_1.parent_id
-
- WHERE folder.folder_id = ? AND folder.account_id = ?
-
-* Previously difficult custom join conditions, like those involving
- functions and/or CASTing of types, will now function as
- expected in most cases::
-
- class HostEntry(Base):
- __tablename__ = "host_entry"
-
- id = Column(Integer, primary_key=True)
- ip_address = Column(INET)
- content = Column(String(50))
-
- # relationship() using explicit foreign_keys, remote_side
- parent_host = relationship(
- "HostEntry",
- primaryjoin=ip_address == cast(content, INET),
- foreign_keys=content,
- remote_side=ip_address,
- )
-
- The new :func:`_orm.relationship` mechanics make use of a
- SQLAlchemy concept known as :term:`annotations`. These annotations
- are also available to application code explicitly via
- the :func:`.foreign` and :func:`.remote` functions, either
- as a means to improve readability for advanced configurations
- or to directly inject an exact configuration, bypassing
- the usual join-inspection heuristics::
-
- from sqlalchemy.orm import foreign, remote
-
-
- class HostEntry(Base):
- __tablename__ = "host_entry"
-
- id = Column(Integer, primary_key=True)
- ip_address = Column(INET)
- content = Column(String(50))
-
- # relationship() using explicit foreign() and remote() annotations
- # in lieu of separate arguments
- parent_host = relationship(
- "HostEntry",
- primaryjoin=remote(ip_address) == cast(foreign(content), INET),
- )
-
-.. seealso::
-
- :ref:`relationship_configure_joins` - a newly revised section on :func:`_orm.relationship`
- detailing the latest techniques for customizing related attributes and collection
- access.
-
-:ticket:`1401` :ticket:`610`
-
-.. _feature_orminspection_08:
-
-New Class/Object Inspection System
-----------------------------------
-
-Lots of SQLAlchemy users are writing systems that require
-the ability to inspect the attributes of a mapped class,
-including being able to get at the primary key columns,
-object relationships, plain attributes, and so forth,
-typically for the purpose of building data-marshalling
-systems, like JSON/XML conversion schemes and of course form
-libraries galore.
-
-Originally, the :class:`_schema.Table` and :class:`_schema.Column` model were the
-original inspection points, which have a well-documented
-system. While SQLAlchemy ORM models are also fully
-introspectable, this has never been a fully stable and
-supported feature, and users tended to not have a clear idea
-how to get at this information.
-
-0.8 now provides a consistent, stable and fully
-documented API for this purpose, including an inspection
-system which works on mapped classes, instances, attributes,
-and other Core and ORM constructs. The entrypoint to this
-system is the core-level :func:`_sa.inspect` function.
-In most cases, the object being inspected
-is one already part of SQLAlchemy's system,
-such as :class:`_orm.Mapper`, :class:`.InstanceState`,
-:class:`_reflection.Inspector`. In some cases, new objects have been
-added with the job of providing the inspection API in
-certain contexts, such as :class:`.AliasedInsp` and
-:class:`.AttributeState`.
-
-A walkthrough of some key capabilities follows:
-
-.. sourcecode:: pycon+sql
-
- >>> class User(Base):
- ... __tablename__ = "user"
- ... id = Column(Integer, primary_key=True)
- ... name = Column(String)
- ... name_syn = synonym(name)
- ... addresses = relationship("Address")
-
- >>> # universal entry point is inspect()
- >>> b = inspect(User)
-
- >>> # b in this case is the Mapper
- >>> b
- <Mapper at 0x101521950; User>
-
- >>> # Column namespace
- >>> b.columns.id
- Column('id', Integer(), table=<user>, primary_key=True, nullable=False)
-
- >>> # mapper's perspective of the primary key
- >>> b.primary_key
- (Column('id', Integer(), table=<user>, primary_key=True, nullable=False),)
-
- >>> # MapperProperties available from .attrs
- >>> b.attrs.keys()
- ['name_syn', 'addresses', 'id', 'name']
-
- >>> # .column_attrs, .relationships, etc. filter this collection
- >>> b.column_attrs.keys()
- ['id', 'name']
-
- >>> list(b.relationships)
- [<sqlalchemy.orm.properties.RelationshipProperty object at 0x1015212d0>]
-
- >>> # they are also namespaces
- >>> b.column_attrs.id
- <sqlalchemy.orm.properties.ColumnProperty object at 0x101525090>
-
- >>> b.relationships.addresses
- <sqlalchemy.orm.properties.RelationshipProperty object at 0x1015212d0>
-
- >>> # point inspect() at a mapped, class level attribute,
- >>> # returns the attribute itself
- >>> b = inspect(User.addresses)
- >>> b
- <sqlalchemy.orm.attributes.InstrumentedAttribute object at 0x101521fd0>
-
- >>> # From here we can get the mapper:
- >>> b.mapper
- <Mapper at 0x101525810; Address>
-
- >>> # the parent inspector, in this case a mapper
- >>> b.parent
- <Mapper at 0x101521950; User>
-
- >>> # an expression
- >>> print(b.expression)
- {printsql}"user".id = address.user_id{stop}
-
- >>> # inspect works on instances
- >>> u1 = User(id=3, name="x")
- >>> b = inspect(u1)
-
- >>> # it returns the InstanceState
- >>> b
- <sqlalchemy.orm.state.InstanceState object at 0x10152bed0>
-
- >>> # similar attrs accessor refers to the
- >>> b.attrs.keys()
- ['id', 'name_syn', 'addresses', 'name']
-
- >>> # attribute interface - from attrs, you get a state object
- >>> b.attrs.id
- <sqlalchemy.orm.state.AttributeState object at 0x10152bf90>
-
- >>> # this object can give you, current value...
- >>> b.attrs.id.value
- 3
-
- >>> # ... current history
- >>> b.attrs.id.history
- History(added=[3], unchanged=(), deleted=())
-
- >>> # InstanceState can also provide session state information
- >>> # lets assume the object is persistent
- >>> s = Session()
- >>> s.add(u1)
- >>> s.commit()
-
- >>> # now we can get primary key identity, always
- >>> # works in query.get()
- >>> b.identity
- (3,)
-
- >>> # the mapper level key
- >>> b.identity_key
- (<class '__main__.User'>, (3,))
-
- >>> # state within the session
- >>> b.persistent, b.transient, b.deleted, b.detached
- (True, False, False, False)
-
- >>> # owning session
- >>> b.session
- <sqlalchemy.orm.session.Session object at 0x101701150>
-
-.. seealso::
-
- :ref:`core_inspection_toplevel`
-
-:ticket:`2208`
-
-New with_polymorphic() feature, can be used anywhere
-----------------------------------------------------
-
-The :meth:`_query.Query.with_polymorphic` method allows the user to
-specify which tables should be present when querying against
-a joined-table entity. Unfortunately the method is awkward
-and only applies to the first entity in the list, and
-otherwise has awkward behaviors both in usage as well as
-within the internals. A new enhancement to the
-:func:`.aliased` construct has been added called
-:func:`.with_polymorphic` which allows any entity to be
-"aliased" into a "polymorphic" version of itself, freely
-usable anywhere:
-
-::
-
- from sqlalchemy.orm import with_polymorphic
-
- palias = with_polymorphic(Person, [Engineer, Manager])
- session.query(Company).join(palias, Company.employees).filter(
- or_(Engineer.language == "java", Manager.hair == "pointy")
- )
-
-.. seealso::
-
- :ref:`with_polymorphic` - newly updated documentation for polymorphic
- loading control.
-
-:ticket:`2333`
-
-of_type() works with alias(), with_polymorphic(), any(), has(), joinedload(), subqueryload(), contains_eager()
---------------------------------------------------------------------------------------------------------------
-
-The :meth:`.PropComparator.of_type` method is used to specify
-a specific subtype to use when constructing SQL expressions along
-a :func:`_orm.relationship` that has a :term:`polymorphic` mapping as its target.
-This method can now be used to target *any number* of target subtypes,
-by combining it with the new :func:`.with_polymorphic` function::
-
- # use eager loading in conjunction with with_polymorphic targets
- Job_P = with_polymorphic(Job, [SubJob, ExtraJob], aliased=True)
- q = (
- s.query(DataContainer)
- .join(DataContainer.jobs.of_type(Job_P))
- .options(contains_eager(DataContainer.jobs.of_type(Job_P)))
- )
-
-The method now works equally well in most places a regular relationship
-attribute is accepted, including with loader functions like
-:func:`_orm.joinedload`, :func:`.subqueryload`, :func:`.contains_eager`,
-and comparison methods like :meth:`.PropComparator.any`
-and :meth:`.PropComparator.has`::
-
- # use eager loading in conjunction with with_polymorphic targets
- Job_P = with_polymorphic(Job, [SubJob, ExtraJob], aliased=True)
- q = (
- s.query(DataContainer)
- .join(DataContainer.jobs.of_type(Job_P))
- .options(contains_eager(DataContainer.jobs.of_type(Job_P)))
- )
-
- # pass subclasses to eager loads (implicitly applies with_polymorphic)
- q = s.query(ParentThing).options(
- joinedload_all(ParentThing.container, DataContainer.jobs.of_type(SubJob))
- )
-
- # control self-referential aliasing with any()/has()
- Job_A = aliased(Job)
- q = (
- s.query(Job)
- .join(DataContainer.jobs)
- .filter(
- DataContainer.jobs.of_type(Job_A).any(
- and_(Job_A.id < Job.id, Job_A.type == "fred")
- )
- )
- )
-
-.. seealso::
-
- :ref:`inheritance_of_type`
-
-:ticket:`2438` :ticket:`1106`
-
-Events Can Be Applied to Unmapped Superclasses
-----------------------------------------------
-
-Mapper and instance events can now be associated with an unmapped
-superclass, where those events will be propagated to subclasses
-as those subclasses are mapped. The ``propagate=True`` flag
-should be used. This feature allows events to be associated
-with a declarative base class::
-
- from sqlalchemy.ext.declarative import declarative_base
-
- Base = declarative_base()
-
-
- @event.listens_for("load", Base, propagate=True)
- def on_load(target, context):
- print("New instance loaded:", target)
-
-
- # on_load() will be applied to SomeClass
- class SomeClass(Base):
- __tablename__ = "sometable"
-
- # ...
-
-:ticket:`2585`
-
-Declarative Distinguishes Between Modules/Packages
---------------------------------------------------
-
-A key feature of Declarative is the ability to refer
-to other mapped classes using their string name. The
-registry of class names is now sensitive to the owning
-module and package of a given class. The classes
-can be referred to via dotted name in expressions::
-
- class Snack(Base):
- # ...
-
- peanuts = relationship(
- "nuts.Peanut", primaryjoin="nuts.Peanut.snack_id == Snack.id"
- )
-
-The resolution allows that any full or partial
-disambiguating package name can be used. If the
-path to a particular class is still ambiguous,
-an error is raised.
-
-:ticket:`2338`
-
-
-New DeferredReflection Feature in Declarative
----------------------------------------------
-
-The "deferred reflection" example has been moved to a
-supported feature within Declarative. This feature allows
-the construction of declarative mapped classes with only
-placeholder ``Table`` metadata, until a ``prepare()`` step
-is called, given an ``Engine`` with which to reflect fully
-all tables and establish actual mappings. The system
-supports overriding of columns, single and joined
-inheritance, as well as distinct bases-per-engine. A full
-declarative configuration can now be created against an
-existing table that is assembled upon engine creation time
-in one step:
-
-::
-
- class ReflectedOne(DeferredReflection, Base):
- __abstract__ = True
-
-
- class ReflectedTwo(DeferredReflection, Base):
- __abstract__ = True
-
-
- class MyClass(ReflectedOne):
- __tablename__ = "mytable"
-
-
- class MyOtherClass(ReflectedOne):
- __tablename__ = "myothertable"
-
-
- class YetAnotherClass(ReflectedTwo):
- __tablename__ = "yetanothertable"
-
-
- ReflectedOne.prepare(engine_one)
- ReflectedTwo.prepare(engine_two)
-
-.. seealso::
-
- :class:`.DeferredReflection`
-
-:ticket:`2485`
-
-ORM Classes Now Accepted by Core Constructs
--------------------------------------------
-
-While the SQL expressions used with :meth:`_query.Query.filter`,
-such as ``User.id == 5``, have always been compatible for
-use with core constructs such as :func:`_expression.select`, the mapped
-class itself would not be recognized when passed to :func:`_expression.select`,
-:meth:`_expression.Select.select_from`, or :meth:`_expression.Select.correlate`.
-A new SQL registration system allows a mapped class to be
-accepted as a FROM clause within the core::
-
- from sqlalchemy import select
-
- stmt = select([User]).where(User.id == 5)
-
-Above, the mapped ``User`` class will expand into
-the :class:`_schema.Table` to which ``User`` is mapped.
-
-:ticket:`2245`
-
-.. _change_orm_2365:
-
-Query.update() supports UPDATE..FROM
-------------------------------------
-
-The new UPDATE..FROM mechanics work in query.update().
-Below, we emit an UPDATE against ``SomeEntity``, adding
-a FROM clause (or equivalent, depending on backend)
-against ``SomeOtherEntity``::
-
- query(SomeEntity).filter(SomeEntity.id == SomeOtherEntity.id).filter(
- SomeOtherEntity.foo == "bar"
- ).update({"data": "x"})
-
-In particular, updates to joined-inheritance
-entities are supported, provided the target of the UPDATE is local to the
-table being filtered on, or if the parent and child tables
-are mixed, they are joined explicitly in the query. Below,
-given ``Engineer`` as a joined subclass of ``Person``:
-
-::
-
- query(Engineer).filter(Person.id == Engineer.id).filter(
- Person.name == "dilbert"
- ).update({"engineer_data": "java"})
-
-would produce:
-
-.. sourcecode:: sql
-
- UPDATE engineer SET engineer_data='java' FROM person
- WHERE person.id=engineer.id AND person.name='dilbert'
-
-:ticket:`2365`
-
-rollback() will only roll back "dirty" objects from a begin_nested()
---------------------------------------------------------------------
-
-A behavioral change that should improve efficiency for those
-users using SAVEPOINT via ``Session.begin_nested()`` - upon
-``rollback()``, only those objects that were made dirty
-since the last flush will be expired, the rest of the
-``Session`` remains intact. This because a ROLLBACK to a
-SAVEPOINT does not terminate the containing transaction's
-isolation, so no expiry is needed except for those changes
-that were not flushed in the current transaction.
-
-:ticket:`2452`
-
-Caching Example now uses dogpile.cache
---------------------------------------
-
-The caching example now uses `dogpile.cache <https://dogpilecache.readthedocs.io/>`_.
-Dogpile.cache is a rewrite of the caching portion
-of Beaker, featuring vastly simpler and faster operation,
-as well as support for distributed locking.
-
-Note that the SQLAlchemy APIs used by the Dogpile example as well
-as the previous Beaker example have changed slightly, in particular
-this change is needed as illustrated in the Beaker example:
-
-.. sourcecode:: diff
-
- --- examples/beaker_caching/caching_query.py
- +++ examples/beaker_caching/caching_query.py
- @@ -222,7 +222,8 @@
-
- """
- if query._current_path:
- - mapper, key = query._current_path[-2:]
- + mapper, prop = query._current_path[-2:]
- + key = prop.key
-
- for cls in mapper.class_.__mro__:
- if (cls, key) in self._relationship_options:
-
-.. seealso::
-
- :ref:`examples_caching`
-
-:ticket:`2589`
-
-New Core Features
-=================
-
-Fully extensible, type-level operator support in Core
------------------------------------------------------
-
-The Core has to date never had any system of adding support
-for new SQL operators to Column and other expression
-constructs, other than the :meth:`.ColumnOperators.op` method
-which is "just enough" to make things work. There has also
-never been any system in place for Core which allows the
-behavior of existing operators to be overridden. Up until
-now, the only way operators could be flexibly redefined was
-in the ORM layer, using :func:`.column_property` given a
-``comparator_factory`` argument. Third party libraries
-like GeoAlchemy therefore were forced to be ORM-centric and
-rely upon an array of hacks to apply new operations as well
-as to get them to propagate correctly.
-
-The new operator system in Core adds the one hook that's
-been missing all along, which is to associate new and
-overridden operators with *types*. Since after all, it's
-not really a column, CAST operator, or SQL function that
-really drives what kinds of operations are present, it's the
-*type* of the expression. The implementation details are
-minimal - only a few extra methods are added to the core
-:class:`_expression.ColumnElement` type so that it consults its
-:class:`.TypeEngine` object for an optional set of operators.
-New or revised operations can be associated with any type,
-either via subclassing of an existing type, by using
-:class:`.TypeDecorator`, or "globally across-the-board" by
-attaching a new :class:`.TypeEngine.Comparator` object to an existing type
-class.
-
-For example, to add logarithm support to :class:`.Numeric` types:
-
-::
-
-
- from sqlalchemy.types import Numeric
- from sqlalchemy.sql import func
-
-
- class CustomNumeric(Numeric):
- class comparator_factory(Numeric.Comparator):
- def log(self, other):
- return func.log(self.expr, other)
-
-The new type is usable like any other type:
-
-::
-
-
- data = Table(
- "data",
- metadata,
- Column("id", Integer, primary_key=True),
- Column("x", CustomNumeric(10, 5)),
- Column("y", CustomNumeric(10, 5)),
- )
-
- stmt = select([data.c.x.log(data.c.y)]).where(data.c.x.log(2) < value)
- print(conn.execute(stmt).fetchall())
-
-New features which have come from this immediately include
-support for PostgreSQL's HSTORE type, as well as new
-operations associated with PostgreSQL's ARRAY
-type. It also paves the way for existing types to acquire
-lots more operators that are specific to those types, such
-as more string, integer and date operators.
-
-.. seealso::
-
- :ref:`types_operators`
-
- :class:`.HSTORE`
-
-:ticket:`2547`
-
-.. _feature_2623:
-
-Multiple-VALUES support for Insert
-----------------------------------
-
-The :meth:`_expression.Insert.values` method now supports a list of dictionaries,
-which will render a multi-VALUES statement such as
-``VALUES (<row1>), (<row2>), ...``. This is only relevant to backends which
-support this syntax, including PostgreSQL, SQLite, and MySQL. It is
-not the same thing as the usual ``executemany()`` style of INSERT which
-remains unchanged::
-
- users.insert().values(
- [
- {"name": "some name"},
- {"name": "some other name"},
- {"name": "yet another name"},
- ]
- )
-
-.. seealso::
-
- :meth:`_expression.Insert.values`
-
-:ticket:`2623`
-
-Type Expressions
-----------------
-
-SQL expressions can now be associated with types. Historically,
-:class:`.TypeEngine` has always allowed Python-side functions which
-receive both bound parameters as well as result row values, passing
-them through a Python side conversion function on the way to/back from
-the database. The new feature allows similar
-functionality, except on the database side::
-
- from sqlalchemy.types import String
- from sqlalchemy import func, Table, Column, MetaData
-
-
- class LowerString(String):
- def bind_expression(self, bindvalue):
- return func.lower(bindvalue)
-
- def column_expression(self, col):
- return func.lower(col)
-
-
- metadata = MetaData()
- test_table = Table("test_table", metadata, Column("data", LowerString))
-
-Above, the ``LowerString`` type defines a SQL expression that will be emitted
-whenever the ``test_table.c.data`` column is rendered in the columns
-clause of a SELECT statement:
-
-.. sourcecode:: pycon+sql
-
- >>> print(select([test_table]).where(test_table.c.data == "HI"))
- {printsql}SELECT lower(test_table.data) AS data
- FROM test_table
- WHERE test_table.data = lower(:data_1)
-
-This feature is also used heavily by the new release of GeoAlchemy,
-to embed PostGIS expressions inline in SQL based on type rules.
-
-.. seealso::
-
- :ref:`types_sql_value_processing`
-
-:ticket:`1534`
-
-Core Inspection System
-----------------------
-
-The :func:`_sa.inspect` function introduced in :ref:`feature_orminspection_08`
-also applies to the core. Applied to an :class:`_engine.Engine` it produces
-an :class:`_reflection.Inspector` object::
-
- from sqlalchemy import inspect
- from sqlalchemy import create_engine
-
- engine = create_engine("postgresql://scott:tiger@localhost/test")
- insp = inspect(engine)
- print(insp.get_table_names())
-
-It can also be applied to any :class:`_expression.ClauseElement`, which returns
-the :class:`_expression.ClauseElement` itself, such as :class:`_schema.Table`, :class:`_schema.Column`,
-:class:`_expression.Select`, etc. This allows it to work fluently between Core
-and ORM constructs.
-
-
-New Method :meth:`_expression.Select.correlate_except`
--------------------------------------------------------
-:func:`_expression.select` now has a method :meth:`_expression.Select.correlate_except`
-which specifies "correlate on all FROM clauses except those
-specified". It can be used for mapping scenarios where
-a related subquery should correlate normally, except
-against a particular target selectable::
-
- class SnortEvent(Base):
- __tablename__ = "event"
-
- id = Column(Integer, primary_key=True)
- signature = Column(Integer, ForeignKey("signature.id"))
-
- signatures = relationship("Signature", lazy=False)
-
-
- class Signature(Base):
- __tablename__ = "signature"
-
- id = Column(Integer, primary_key=True)
-
- sig_count = column_property(
- select([func.count("*")])
- .where(SnortEvent.signature == id)
- .correlate_except(SnortEvent)
- )
-
-.. seealso::
-
- :meth:`_expression.Select.correlate_except`
-
-PostgreSQL HSTORE type
-----------------------
-
-Support for PostgreSQL's ``HSTORE`` type is now available as
-:class:`_postgresql.HSTORE`. This type makes great usage
-of the new operator system to provide a full range of operators
-for HSTORE types, including index access, concatenation,
-and containment methods such as
-:meth:`~.HSTORE.comparator_factory.has_key`,
-:meth:`~.HSTORE.comparator_factory.has_any`, and
-:meth:`~.HSTORE.comparator_factory.matrix`::
-
- from sqlalchemy.dialects.postgresql import HSTORE
-
- data = Table(
- "data_table",
- metadata,
- Column("id", Integer, primary_key=True),
- Column("hstore_data", HSTORE),
- )
-
- engine.execute(select([data.c.hstore_data["some_key"]])).scalar()
-
- engine.execute(select([data.c.hstore_data.matrix()])).scalar()
-
-.. seealso::
-
- :class:`_postgresql.HSTORE`
-
- :class:`_postgresql.hstore`
-
-:ticket:`2606`
-
-Enhanced PostgreSQL ARRAY type
-------------------------------
-
-The :class:`_postgresql.ARRAY` type will accept an optional
-"dimension" argument, pinning it to a fixed number of
-dimensions and greatly improving efficiency when retrieving
-results:
-
-::
-
- # old way, still works since PG supports N-dimensions per row:
- Column("my_array", postgresql.ARRAY(Integer))
-
- # new way, will render ARRAY with correct number of [] in DDL,
- # will process binds and results more efficiently as we don't need
- # to guess how many levels deep to go
- Column("my_array", postgresql.ARRAY(Integer, dimensions=2))
-
-The type also introduces new operators, using the new type-specific
-operator framework. New operations include indexed access::
-
- result = conn.execute(select([mytable.c.arraycol[2]]))
-
-slice access in SELECT::
-
- result = conn.execute(select([mytable.c.arraycol[2:4]]))
-
-slice updates in UPDATE::
-
- conn.execute(mytable.update().values({mytable.c.arraycol[2:3]: [7, 8]}))
-
-freestanding array literals::
-
- >>> from sqlalchemy.dialects import postgresql
- >>> conn.scalar(select([postgresql.array([1, 2]) + postgresql.array([3, 4, 5])]))
- [1, 2, 3, 4, 5]
-
-array concatenation, where below, the right side ``[4, 5, 6]`` is coerced into an array literal::
-
- select([mytable.c.arraycol + [4, 5, 6]])
-
-.. seealso::
-
- :class:`_postgresql.ARRAY`
-
- :class:`_postgresql.array`
-
-:ticket:`2441`
-
-New, configurable DATE, TIME types for SQLite
----------------------------------------------
-
-SQLite has no built-in DATE, TIME, or DATETIME types, and
-instead provides some support for storage of date and time
-values either as strings or integers. The date and time
-types for SQLite are enhanced in 0.8 to be much more
-configurable as to the specific format, including that the
-"microseconds" portion is optional, as well as pretty much
-everything else.
-
-::
-
- Column("sometimestamp", sqlite.DATETIME(truncate_microseconds=True))
- Column(
- "sometimestamp",
- sqlite.DATETIME(
- storage_format=(
- "%(year)04d%(month)02d%(day)02d"
- "%(hour)02d%(minute)02d%(second)02d%(microsecond)06d"
- ),
- regexp="(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(\d{6})",
- ),
- )
- Column(
- "somedate",
- sqlite.DATE(
- storage_format="%(month)02d/%(day)02d/%(year)04d",
- regexp="(?P<month>\d+)/(?P<day>\d+)/(?P<year>\d+)",
- ),
- )
-
-Huge thanks to Nate Dub for the sprinting on this at Pycon 2012.
-
-.. seealso::
-
- :class:`_sqlite.DATETIME`
-
- :class:`_sqlite.DATE`
-
- :class:`_sqlite.TIME`
-
-:ticket:`2363`
-
-"COLLATE" supported across all dialects; in particular MySQL, PostgreSQL, SQLite
---------------------------------------------------------------------------------
-
-The "collate" keyword, long accepted by the MySQL dialect, is now established
-on all :class:`.String` types and will render on any backend, including
-when features such as :meth:`_schema.MetaData.create_all` and :func:`.cast` is used:
-
-.. sourcecode:: pycon+sql
-
- >>> stmt = select([cast(sometable.c.somechar, String(20, collation="utf8"))])
- >>> print(stmt)
- {printsql}SELECT CAST(sometable.somechar AS VARCHAR(20) COLLATE "utf8") AS anon_1
- FROM sometable
-
-.. seealso::
-
- :class:`.String`
-
-:ticket:`2276`
-
-"Prefixes" now supported for :func:`_expression.update`, :func:`_expression.delete`
-------------------------------------------------------------------------------------
-Geared towards MySQL, a "prefix" can be rendered within any of
-these constructs. E.g.::
-
- stmt = table.delete().prefix_with("LOW_PRIORITY", dialect="mysql")
-
-
- stmt = table.update().prefix_with("LOW_PRIORITY", dialect="mysql")
-
-The method is new in addition to those which already existed
-on :func:`_expression.insert`, :func:`_expression.select` and :class:`_query.Query`.
-
-.. seealso::
-
- :meth:`_expression.Update.prefix_with`
-
- :meth:`_expression.Delete.prefix_with`
-
- :meth:`_expression.Insert.prefix_with`
-
- :meth:`_expression.Select.prefix_with`
-
- :meth:`_query.Query.prefix_with`
-
-:ticket:`2431`
-
-
-Behavioral Changes
-==================
-
-.. _legacy_is_orphan_addition:
-
-The consideration of a "pending" object as an "orphan" has been made more aggressive
-------------------------------------------------------------------------------------
-
-This is a late add to the 0.8 series, however it is hoped that the new behavior
-is generally more consistent and intuitive in a wider variety of
-situations. The ORM has since at least version 0.4 included behavior
-such that an object that's "pending", meaning that it's
-associated with a :class:`.Session` but hasn't been inserted into the database
-yet, is automatically expunged from the :class:`.Session` when it becomes an "orphan",
-which means it has been de-associated with a parent object that refers to it
-with ``delete-orphan`` cascade on the configured :func:`_orm.relationship`. This
-behavior is intended to approximately mirror the behavior of a persistent
-(that is, already inserted) object, where the ORM will emit a DELETE for such
-objects that become orphans based on the interception of detachment events.
-
-The behavioral change comes into play for objects that
-are referred to by multiple kinds of parents that each specify ``delete-orphan``; the
-typical example is an :ref:`association object <association_pattern>` that bridges two other kinds of objects
-in a many-to-many pattern. Previously, the behavior was such that the
-pending object would be expunged only when de-associated with *all* of its parents.
-With the behavioral change, the pending object
-is expunged as soon as it is de-associated from *any* of the parents that it was
-previously associated with. This behavior is intended to more closely
-match that of persistent objects, which are deleted as soon
-as they are de-associated from any parent.
-
-The rationale for the older behavior dates back
-at least to version 0.4, and was basically a defensive decision to try to alleviate
-confusion when an object was still being constructed for INSERT. But the reality
-is that the object is re-associated with the :class:`.Session` as soon as it is
-attached to any new parent in any case.
-
-It's still possible to flush an object
-that is not associated with all of its required parents, if the object was either
-not associated with those parents in the first place, or if it was expunged, but then
-re-associated with a :class:`.Session` via a subsequent attachment event but still
-not fully associated. In this situation, it is expected that the database
-would emit an integrity error, as there are likely NOT NULL foreign key columns
-that are unpopulated. The ORM makes the decision to let these INSERT attempts
-occur, based on the judgment that an object that is only partially associated with
-its required parents but has been actively associated with some of them,
-is more often than not a user error, rather than an intentional
-omission which should be silently skipped - silently skipping the INSERT here would
-make user errors of this nature very hard to debug.
-
-The old behavior, for applications that might have been relying upon it, can be re-enabled for
-any :class:`_orm.Mapper` by specifying the flag ``legacy_is_orphan`` as a mapper
-option.
-
-The new behavior allows the following test case to work::
-
- from sqlalchemy import Column, Integer, String, ForeignKey
- from sqlalchemy.orm import relationship, backref
- from sqlalchemy.ext.declarative import declarative_base
-
- Base = declarative_base()
-
-
- class User(Base):
- __tablename__ = "user"
- id = Column(Integer, primary_key=True)
- name = Column(String(64))
-
-
- class UserKeyword(Base):
- __tablename__ = "user_keyword"
- user_id = Column(Integer, ForeignKey("user.id"), primary_key=True)
- keyword_id = Column(Integer, ForeignKey("keyword.id"), primary_key=True)
-
- user = relationship(
- User, backref=backref("user_keywords", cascade="all, delete-orphan")
- )
-
- keyword = relationship(
- "Keyword", backref=backref("user_keywords", cascade="all, delete-orphan")
- )
-
- # uncomment this to enable the old behavior
- # __mapper_args__ = {"legacy_is_orphan": True}
-
-
- class Keyword(Base):
- __tablename__ = "keyword"
- id = Column(Integer, primary_key=True)
- keyword = Column("keyword", String(64))
-
-
- from sqlalchemy import create_engine
- from sqlalchemy.orm import Session
-
- # note we're using PostgreSQL to ensure that referential integrity
- # is enforced, for demonstration purposes.
- e = create_engine("postgresql://scott:tiger@localhost/test", echo=True)
-
- Base.metadata.drop_all(e)
- Base.metadata.create_all(e)
-
- session = Session(e)
-
- u1 = User(name="u1")
- k1 = Keyword(keyword="k1")
-
- session.add_all([u1, k1])
-
- uk1 = UserKeyword(keyword=k1, user=u1)
-
- # previously, if session.flush() were called here,
- # this operation would succeed, but if session.flush()
- # were not called here, the operation fails with an
- # integrity error.
- # session.flush()
- del u1.user_keywords[0]
-
- session.commit()
-
-:ticket:`2655`
-
-The after_attach event fires after the item is associated with the Session instead of before; before_attach added
------------------------------------------------------------------------------------------------------------------
-
-Event handlers which use after_attach can now assume the
-given instance is associated with the given session:
-
-::
-
- @event.listens_for(Session, "after_attach")
- def after_attach(session, instance):
- assert instance in session
-
-Some use cases require that it work this way. However,
-other use cases require that the item is *not* yet part of
-the session, such as when a query, intended to load some
-state required for an instance, emits autoflush first and
-would otherwise prematurely flush the target object. Those
-use cases should use the new "before_attach" event:
-
-::
-
- @event.listens_for(Session, "before_attach")
- def before_attach(session, instance):
- instance.some_necessary_attribute = (
- session.query(Widget).filter_by(instance.widget_name).first()
- )
-
-:ticket:`2464`
-
-
-
-Query now auto-correlates like a select() does
-----------------------------------------------
-
-Previously it was necessary to call :meth:`_query.Query.correlate` in
-order to have a column- or WHERE-subquery correlate to the
-parent:
-
-::
-
- subq = (
- session.query(Entity.value)
- .filter(Entity.id == Parent.entity_id)
- .correlate(Parent)
- .as_scalar()
- )
- session.query(Parent).filter(subq == "some value")
-
-This was the opposite behavior of a plain ``select()``
-construct which would assume auto-correlation by default.
-The above statement in 0.8 will correlate automatically:
-
-::
-
- subq = session.query(Entity.value).filter(Entity.id == Parent.entity_id).as_scalar()
- session.query(Parent).filter(subq == "some value")
-
-like in ``select()``, correlation can be disabled by calling
-``query.correlate(None)`` or manually set by passing an
-entity, ``query.correlate(someentity)``.
-
-:ticket:`2179`
-
-.. _correlation_context_specific:
-
-Correlation is now always context-specific
-------------------------------------------
-
-To allow a wider variety of correlation scenarios, the behavior of
-:meth:`_expression.Select.correlate` and :meth:`_query.Query.correlate` has changed slightly
-such that the SELECT statement will omit the "correlated" target from the
-FROM clause only if the statement is actually used in that context. Additionally,
-it's no longer possible for a SELECT statement that's placed as a FROM
-in an enclosing SELECT statement to "correlate" (i.e. omit) a FROM clause.
-
-This change only makes things better as far as rendering SQL, in that it's no
-longer possible to render illegal SQL where there are insufficient FROM
-objects relative to what's being selected::
-
- from sqlalchemy.sql import table, column, select
-
- t1 = table("t1", column("x"))
- t2 = table("t2", column("y"))
- s = select([t1, t2]).correlate(t1)
-
- print(s)
-
-Prior to this change, the above would return:
-
-.. sourcecode:: sql
-
- SELECT t1.x, t2.y FROM t2
-
-which is invalid SQL as "t1" is not referred to in any FROM clause.
-
-Now, in the absence of an enclosing SELECT, it returns:
-
-.. sourcecode:: sql
-
- SELECT t1.x, t2.y FROM t1, t2
-
-Within a SELECT, the correlation takes effect as expected:
-
-.. sourcecode:: python
-
- s2 = select([t1, t2]).where(t1.c.x == t2.c.y).where(t1.c.x == s)
- print(s2)
-
-.. sourcecode:: sql
-
- SELECT t1.x, t2.y FROM t1, t2
- WHERE t1.x = t2.y AND t1.x =
- (SELECT t1.x, t2.y FROM t2)
-
-This change is not expected to impact any existing applications, as
-the correlation behavior remains identical for properly constructed
-expressions. Only an application that relies, most likely within a
-testing scenario, on the invalid string output of a correlated
-SELECT used in a non-correlating context would see any change.
-
-:ticket:`2668`
-
-
-.. _metadata_create_drop_tables:
-
-create_all() and drop_all() will now honor an empty list as such
-----------------------------------------------------------------
-
-The methods :meth:`_schema.MetaData.create_all` and :meth:`_schema.MetaData.drop_all`
-will now accept a list of :class:`_schema.Table` objects that is empty,
-and will not emit any CREATE or DROP statements. Previously,
-an empty list was interpreted the same as passing ``None``
-for a collection, and CREATE/DROP would be emitted for all
-items unconditionally.
-
-This is a bug fix but some applications may have been relying upon
-the previous behavior.
-
-:ticket:`2664`
-
-Repaired the Event Targeting of :class:`.InstrumentationEvents`
----------------------------------------------------------------
-
-The :class:`.InstrumentationEvents` series of event targets have
-documented that the events will only be fired off according to
-the actual class passed as a target. Through 0.7, this wasn't the
-case, and any event listener applied to :class:`.InstrumentationEvents`
-would be invoked for all classes mapped. In 0.8, additional
-logic has been added so that the events will only invoke for those
-classes sent in. The ``propagate`` flag here is set to ``True``
-by default as class instrumentation events are typically used to
-intercept classes that aren't yet created.
-
-:ticket:`2590`
-
-No more magic coercion of "=" to IN when comparing to subquery in MS-SQL
-------------------------------------------------------------------------
-
-We found a very old behavior in the MSSQL dialect which
-would attempt to rescue users from themselves when
-doing something like this:
-
-::
-
- scalar_subq = select([someothertable.c.id]).where(someothertable.c.data == "foo")
- select([sometable]).where(sometable.c.id == scalar_subq)
-
-SQL Server doesn't allow an equality comparison to a scalar
-SELECT, that is, "x = (SELECT something)". The MSSQL dialect
-would convert this to an IN. The same thing would happen
-however upon a comparison like "(SELECT something) = x", and
-overall this level of guessing is outside of SQLAlchemy's
-usual scope so the behavior is removed.
-
-:ticket:`2277`
-
-Fixed the behavior of :meth:`.Session.is_modified`
---------------------------------------------------
-
-The :meth:`.Session.is_modified` method accepts an argument
-``passive`` which basically should not be necessary, the
-argument in all cases should be the value ``True`` - when
-left at its default of ``False`` it would have the effect of
-hitting the database, and often triggering autoflush which
-would itself change the results. In 0.8 the ``passive``
-argument will have no effect, and unloaded attributes will
-never be checked for history since by definition there can
-be no pending state change on an unloaded attribute.
-
-.. seealso::
-
- :meth:`.Session.is_modified`
-
-:ticket:`2320`
-
-:attr:`_schema.Column.key` is honored in the :attr:`_expression.Select.c` attribute of :func:`_expression.select` with :meth:`_expression.Select.apply_labels`
----------------------------------------------------------------------------------------------------------------------------------------------------------------
-Users of the expression system know that :meth:`_expression.Select.apply_labels`
-prepends the table name to each column name, affecting the
-names that are available from :attr:`_expression.Select.c`:
-
-::
-
- s = select([table1]).apply_labels()
- s.c.table1_col1
- s.c.table1_col2
-
-Before 0.8, if the :class:`_schema.Column` had a different :attr:`_schema.Column.key`, this
-key would be ignored, inconsistently versus when
-:meth:`_expression.Select.apply_labels` were not used:
-
-::
-
- # before 0.8
- table1 = Table("t1", metadata, Column("col1", Integer, key="column_one"))
- s = select([table1])
- s.c.column_one # would be accessible like this
- s.c.col1 # would raise AttributeError
-
- s = select([table1]).apply_labels()
- s.c.table1_column_one # would raise AttributeError
- s.c.table1_col1 # would be accessible like this
-
-In 0.8, :attr:`_schema.Column.key` is honored in both cases:
-
-::
-
- # with 0.8
- table1 = Table("t1", metadata, Column("col1", Integer, key="column_one"))
- s = select([table1])
- s.c.column_one # works
- s.c.col1 # AttributeError
-
- s = select([table1]).apply_labels()
- s.c.table1_column_one # works
- s.c.table1_col1 # AttributeError
-
-All other behavior regarding "name" and "key" are the same,
-including that the rendered SQL will still use the form
-``<tablename>_<colname>`` - the emphasis here was on
-preventing the :attr:`_schema.Column.key` contents from being rendered into the
-``SELECT`` statement so that there are no issues with
-special/ non-ascii characters used in the :attr:`_schema.Column.key`.
-
-:ticket:`2397`
-
-single_parent warning is now an error
--------------------------------------
-
-A :func:`_orm.relationship` that is many-to-one or many-to-many and
-specifies "cascade='all, delete-orphan'", which is an
-awkward but nonetheless supported use case (with
-restrictions) will now raise an error if the relationship
-does not specify the ``single_parent=True`` option.
-Previously it would only emit a warning, but a failure would
-follow almost immediately within the attribute system in any
-case.
-
-:ticket:`2405`
-
-Adding the ``inspector`` argument to the ``column_reflect`` event
------------------------------------------------------------------
-
-0.7 added a new event called ``column_reflect``, provided so
-that the reflection of columns could be augmented as each
-one were reflected. We got this event slightly wrong in
-that the event gave no way to get at the current
-``Inspector`` and ``Connection`` being used for the
-reflection, in the case that additional information from the
-database is needed. As this is a new event not widely used
-yet, we'll be adding the ``inspector`` argument into it
-directly::
-
- @event.listens_for(Table, "column_reflect")
- def listen_for_col(inspector, table, column_info):
- ...
-
-:ticket:`2418`
-
-Disabling auto-detect of collations, casing for MySQL
------------------------------------------------------
-
-The MySQL dialect does two calls, one very expensive, to
-load all possible collations from the database as well as
-information on casing, the first time an ``Engine``
-connects. Neither of these collections are used for any
-SQLAlchemy functions, so these calls will be changed to no
-longer be emitted automatically. Applications that might
-have relied on these collections being present on
-``engine.dialect`` will need to call upon
-``_detect_collations()`` and ``_detect_casing()`` directly.
-
-:ticket:`2404`
-
-"Unconsumed column names" warning becomes an exception
-------------------------------------------------------
-
-Referring to a non-existent column in an ``insert()`` or
-``update()`` construct will raise an error instead of a
-warning:
-
-::
-
- t1 = table("t1", column("x"))
- t1.insert().values(x=5, z=5) # raises "Unconsumed column names: z"
-
-:ticket:`2415`
-
-Inspector.get_primary_keys() is deprecated, use Inspector.get_pk_constraint
----------------------------------------------------------------------------
-
-These two methods on ``Inspector`` were redundant, where
-``get_primary_keys()`` would return the same information as
-``get_pk_constraint()`` minus the name of the constraint:
-
-::
-
- >>> insp.get_primary_keys()
- ["a", "b"]
-
- >>> insp.get_pk_constraint()
- {"name":"pk_constraint", "constrained_columns":["a", "b"]}
-
-:ticket:`2422`
-
-Case-insensitive result row names will be disabled in most cases
-----------------------------------------------------------------
-
-A very old behavior, the column names in ``RowProxy`` were
-always compared case-insensitively:
-
-::
-
- >>> row = result.fetchone()
- >>> row["foo"] == row["FOO"] == row["Foo"]
- True
-
-This was for the benefit of a few dialects which in the
-early days needed this, like Oracle and Firebird, but in
-modern usage we have more accurate ways of dealing with the
-case-insensitive behavior of these two platforms.
-
-Going forward, this behavior will be available only
-optionally, by passing the flag ```case_sensitive=False```
-to ```create_engine()```, but otherwise column names
-requested from the row must match as far as casing.
-
-:ticket:`2423`
-
-``InstrumentationManager`` and alternate class instrumentation is now an extension
-----------------------------------------------------------------------------------
-
-The ``sqlalchemy.orm.interfaces.InstrumentationManager``
-class is moved to
-``sqlalchemy.ext.instrumentation.InstrumentationManager``.
-The "alternate instrumentation" system was built for the
-benefit of a very small number of installations that needed
-to work with existing or unusual class instrumentation
-systems, and generally is very seldom used. The complexity
-of this system has been exported to an ``ext.`` module. It
-remains unused until once imported, typically when a third
-party library imports ``InstrumentationManager``, at which
-point it is injected back into ``sqlalchemy.orm`` by
-replacing the default ``InstrumentationFactory`` with
-``ExtendedInstrumentationRegistry``.
-
-Removed
-=======
-
-SQLSoup
--------
-
-SQLSoup is a handy package that presents an alternative
-interface on top of the SQLAlchemy ORM. SQLSoup is now
-moved into its own project and documented/released
-separately; see https://bitbucket.org/zzzeek/sqlsoup.
-
-SQLSoup is a very simple tool that could also benefit from
-contributors who are interested in its style of usage.
-
-:ticket:`2262`
-
-MutableType
------------
-
-The older "mutable" system within the SQLAlchemy ORM has
-been removed. This refers to the ``MutableType`` interface
-which was applied to types such as ``PickleType`` and
-conditionally to ``TypeDecorator``, and since very early
-SQLAlchemy versions has provided a way for the ORM to detect
-changes in so-called "mutable" data structures such as JSON
-structures and pickled objects. However, the
-implementation was never reasonable and forced a very
-inefficient mode of usage on the unit-of-work which caused
-an expensive scan of all objects to take place during flush.
-In 0.7, the `sqlalchemy.ext.mutable <https://docs.sqlalchemy.
-org/en/latest/orm/extensions/mutable.html>`_ extension was
-introduced so that user-defined datatypes can appropriately
-send events to the unit of work as changes occur.
-
-Today, usage of ``MutableType`` is expected to be low, as
-warnings have been in place for some years now regarding its
-inefficiency.
-
-:ticket:`2442`
-
-sqlalchemy.exceptions (has been sqlalchemy.exc for years)
----------------------------------------------------------
-
-We had left in an alias ``sqlalchemy.exceptions`` to attempt
-to make it slightly easier for some very old libraries that
-hadn't yet been upgraded to use ``sqlalchemy.exc``. Some
-users are still being confused by it however so in 0.8 we're
-taking it out entirely to eliminate any of that confusion.
-
-:ticket:`2433`
-
+++ /dev/null
-==============================
-What's New in SQLAlchemy 0.9?
-==============================
-
-.. admonition:: About this Document
-
- This document describes changes between SQLAlchemy version 0.8,
- undergoing maintenance releases as of May, 2013,
- and SQLAlchemy version 0.9, which had its first production
- release on December 30, 2013.
-
- Document last updated: June 10, 2015
-
-Introduction
-============
-
-This guide introduces what's new in SQLAlchemy version 0.9,
-and also documents changes which affect users migrating
-their applications from the 0.8 series of SQLAlchemy to 0.9.
-
-Please carefully review
-:ref:`behavioral_changes_orm_09` and :ref:`behavioral_changes_core_09` for
-potentially backwards-incompatible changes.
-
-Platform Support
-================
-
-Targeting Python 2.6 and Up Now, Python 3 without 2to3
--------------------------------------------------------
-
-The first achievement of the 0.9 release is to remove the dependency
-on the 2to3 tool for Python 3 compatibility. To make this
-more straightforward, the lowest Python release targeted now
-is 2.6, which features a wide degree of cross-compatibility with
-Python 3. All SQLAlchemy modules and unit tests are now interpreted
-equally well with any Python interpreter from 2.6 forward, including
-the 3.1 and 3.2 interpreters.
-
-:ticket:`2671`
-
-C Extensions Supported on Python 3
------------------------------------
-
-The C extensions have been ported to support Python 3 and now build
-in both Python 2 and Python 3 environments.
-
-:ticket:`2161`
-
-.. _behavioral_changes_orm_09:
-
-Behavioral Changes - ORM
-========================
-
-.. _migration_2824:
-
-Composite attributes are now returned as their object form when queried on a per-attribute basis
-------------------------------------------------------------------------------------------------
-
-Using a :class:`_query.Query` in conjunction with a composite attribute now returns the object
-type maintained by that composite, rather than being broken out into individual
-columns. Using the mapping setup at :ref:`mapper_composite`::
-
- >>> session.query(Vertex.start, Vertex.end).filter(Vertex.start == Point(3, 4)).all()
- [(Point(x=3, y=4), Point(x=5, y=6))]
-
-This change is backwards-incompatible with code that expects the individual attribute
-to be expanded into individual columns. To get that behavior, use the ``.clauses``
-accessor::
-
-
- >>> session.query(Vertex.start.clauses, Vertex.end.clauses).filter(
- ... Vertex.start == Point(3, 4)
- ... ).all()
- [(3, 4, 5, 6)]
-
-.. seealso::
-
- :ref:`change_2824`
-
-:ticket:`2824`
-
-
-.. _migration_2736:
-
-:meth:`_query.Query.select_from` no longer applies the clause to corresponding entities
-----------------------------------------------------------------------------------------
-The :meth:`_query.Query.select_from` method has been popularized in recent versions
-as a means of controlling the first thing that a :class:`_query.Query` object
-"selects from", typically for the purposes of controlling how a JOIN will
-render.
-
-Consider the following example against the usual ``User`` mapping::
-
- select_stmt = select([User]).where(User.id == 7).alias()
-
- q = (
- session.query(User)
- .join(select_stmt, User.id == select_stmt.c.id)
- .filter(User.name == "ed")
- )
-
-The above statement predictably renders SQL like the following:
-
-.. sourcecode:: sql
-
- SELECT "user".id AS user_id, "user".name AS user_name
- FROM "user" JOIN (SELECT "user".id AS id, "user".name AS name
- FROM "user"
- WHERE "user".id = :id_1) AS anon_1 ON "user".id = anon_1.id
- WHERE "user".name = :name_1
-
-If we wanted to reverse the order of the left and right elements of the
-JOIN, the documentation would lead us to believe we could use
-:meth:`_query.Query.select_from` to do so::
-
- q = (
- session.query(User)
- .select_from(select_stmt)
- .join(User, User.id == select_stmt.c.id)
- .filter(User.name == "ed")
- )
-
-However, in version 0.8 and earlier, the above use of :meth:`_query.Query.select_from`
-would apply the ``select_stmt`` to **replace** the ``User`` entity, as it
-selects from the ``user`` table which is compatible with ``User``:
-
-.. sourcecode:: sql
-
- -- SQLAlchemy 0.8 and earlier...
- SELECT anon_1.id AS anon_1_id, anon_1.name AS anon_1_name
- FROM (SELECT "user".id AS id, "user".name AS name
- FROM "user"
- WHERE "user".id = :id_1) AS anon_1 JOIN "user" ON anon_1.id = anon_1.id
- WHERE anon_1.name = :name_1
-
-The above statement is a mess, the ON clause refers ``anon_1.id = anon_1.id``,
-our WHERE clause has been replaced with ``anon_1`` as well.
-
-This behavior is quite intentional, but has a different use case from that
-which has become popular for :meth:`_query.Query.select_from`. The above behavior
-is now available by a new method known as :meth:`_query.Query.select_entity_from`.
-This is a lesser used behavior that in modern SQLAlchemy is roughly equivalent
-to selecting from a customized :func:`.aliased` construct::
-
- select_stmt = select([User]).where(User.id == 7)
- user_from_stmt = aliased(User, select_stmt.alias())
-
- q = session.query(user_from_stmt).filter(user_from_stmt.name == "ed")
-
-So with SQLAlchemy 0.9, our query that selects from ``select_stmt`` produces
-the SQL we expect:
-
-.. sourcecode:: sql
-
- -- SQLAlchemy 0.9
- SELECT "user".id AS user_id, "user".name AS user_name
- FROM (SELECT "user".id AS id, "user".name AS name
- FROM "user"
- WHERE "user".id = :id_1) AS anon_1 JOIN "user" ON "user".id = id
- WHERE "user".name = :name_1
-
-The :meth:`_query.Query.select_entity_from` method will be available in SQLAlchemy
-**0.8.2**, so applications which rely on the old behavior can transition
-to this method first, ensure all tests continue to function, then upgrade
-to 0.9 without issue.
-
-:ticket:`2736`
-
-
-.. _migration_2833:
-
-``viewonly=True`` on ``relationship()`` prevents history from taking effect
----------------------------------------------------------------------------
-
-The ``viewonly`` flag on :func:`_orm.relationship` is applied to prevent changes
-to the target attribute from having any effect within the flush process.
-This is achieved by eliminating the attribute from being considered during
-the flush. However, up until now, changes to the attribute would still
-register the parent object as "dirty" and trigger a potential flush. The change
-is that the ``viewonly`` flag now prevents history from being set for the
-target attribute as well. Attribute events like backrefs and user-defined events
-still continue to function normally.
-
-The change is illustrated as follows::
-
- from sqlalchemy import Column, Integer, ForeignKey, create_engine
- from sqlalchemy.orm import backref, relationship, Session
- from sqlalchemy.ext.declarative import declarative_base
- from sqlalchemy import inspect
-
- Base = declarative_base()
-
-
- class A(Base):
- __tablename__ = "a"
- id = Column(Integer, primary_key=True)
-
-
- class B(Base):
- __tablename__ = "b"
-
- id = Column(Integer, primary_key=True)
- a_id = Column(Integer, ForeignKey("a.id"))
- a = relationship("A", backref=backref("bs", viewonly=True))
-
-
- e = create_engine("sqlite://")
- Base.metadata.create_all(e)
-
- a = A()
- b = B()
-
- sess = Session(e)
- sess.add_all([a, b])
- sess.commit()
-
- b.a = a
-
- assert b in sess.dirty
-
- # before 0.9.0
- # assert a in sess.dirty
- # assert inspect(a).attrs.bs.history.has_changes()
-
- # after 0.9.0
- assert a not in sess.dirty
- assert not inspect(a).attrs.bs.history.has_changes()
-
-:ticket:`2833`
-
-.. _migration_2751:
-
-Association Proxy SQL Expression Improvements and Fixes
--------------------------------------------------------
-
-The ``==`` and ``!=`` operators as implemented by an association proxy
-that refers to a scalar value on a scalar relationship now produces
-a more complete SQL expression, intended to take into account
-the "association" row being present or not when the comparison is against
-``None``.
-
-Consider this mapping::
-
- class A(Base):
- __tablename__ = "a"
-
- id = Column(Integer, primary_key=True)
-
- b_id = Column(Integer, ForeignKey("b.id"), primary_key=True)
- b = relationship("B")
- b_value = association_proxy("b", "value")
-
-
- class B(Base):
- __tablename__ = "b"
- id = Column(Integer, primary_key=True)
- value = Column(String)
-
-Up through 0.8, a query like the following::
-
- s.query(A).filter(A.b_value == None).all()
-
-would produce:
-
-.. sourcecode:: sql
-
- SELECT a.id AS a_id, a.b_id AS a_b_id
- FROM a
- WHERE EXISTS (SELECT 1
- FROM b
- WHERE b.id = a.b_id AND b.value IS NULL)
-
-In 0.9, it now produces:
-
-.. sourcecode:: sql
-
- SELECT a.id AS a_id, a.b_id AS a_b_id
- FROM a
- WHERE (EXISTS (SELECT 1
- FROM b
- WHERE b.id = a.b_id AND b.value IS NULL)) OR a.b_id IS NULL
-
-The difference being, it not only checks ``b.value``, it also checks
-if ``a`` refers to no ``b`` row at all. This will return different
-results versus prior versions, for a system that uses this type of
-comparison where some parent rows have no association row.
-
-More critically, a correct expression is emitted for ``A.b_value != None``.
-In 0.8, this would return ``True`` for ``A`` rows that had no ``b``:
-
-.. sourcecode:: sql
-
- SELECT a.id AS a_id, a.b_id AS a_b_id
- FROM a
- WHERE NOT (EXISTS (SELECT 1
- FROM b
- WHERE b.id = a.b_id AND b.value IS NULL))
-
-Now in 0.9, the check has been reworked so that it ensures
-the A.b_id row is present, in addition to ``B.value`` being
-non-NULL:
-
-.. sourcecode:: sql
-
- SELECT a.id AS a_id, a.b_id AS a_b_id
- FROM a
- WHERE EXISTS (SELECT 1
- FROM b
- WHERE b.id = a.b_id AND b.value IS NOT NULL)
-
-In addition, the ``has()`` operator is enhanced such that you can
-call it against a scalar column value with no criterion only,
-and it will produce criteria that checks for the association row
-being present or not::
-
- s.query(A).filter(A.b_value.has()).all()
-
-output:
-
-.. sourcecode:: sql
-
- SELECT a.id AS a_id, a.b_id AS a_b_id
- FROM a
- WHERE EXISTS (SELECT 1
- FROM b
- WHERE b.id = a.b_id)
-
-This is equivalent to ``A.b.has()``, but allows one to query
-against ``b_value`` directly.
-
-:ticket:`2751`
-
-.. _migration_2810:
-
-Association Proxy Missing Scalar returns None
----------------------------------------------
-
-An association proxy from a scalar attribute to a scalar will now return
-``None`` if the proxied object isn't present. This is consistent with the
-fact that missing many-to-ones return None in SQLAlchemy, so should the
-proxied value. E.g.::
-
- from sqlalchemy import *
- from sqlalchemy.orm import *
- from sqlalchemy.ext.declarative import declarative_base
- from sqlalchemy.ext.associationproxy import association_proxy
-
- Base = declarative_base()
-
-
- class A(Base):
- __tablename__ = "a"
-
- id = Column(Integer, primary_key=True)
- b = relationship("B", uselist=False)
-
- bname = association_proxy("b", "name")
-
-
- class B(Base):
- __tablename__ = "b"
-
- id = Column(Integer, primary_key=True)
- a_id = Column(Integer, ForeignKey("a.id"))
- name = Column(String)
-
-
- a1 = A()
-
- # this is how m2o's always have worked
- assert a1.b is None
-
- # but prior to 0.9, this would raise AttributeError,
- # now returns None just like the proxied value.
- assert a1.bname is None
-
-:ticket:`2810`
-
-
-.. _change_2787:
-
-attributes.get_history() will query from the DB by default if value not present
--------------------------------------------------------------------------------
-
-A bugfix regarding :func:`.attributes.get_history` allows a column-based attribute
-to query out to the database for an unloaded value, assuming the ``passive``
-flag is left at its default of ``PASSIVE_OFF``. Previously, this flag would
-not be honored. Additionally, a new method :meth:`.AttributeState.load_history`
-is added to complement the :attr:`.AttributeState.history` attribute, which
-will emit loader callables for an unloaded attribute.
-
-This is a small change demonstrated as follows::
-
- from sqlalchemy import Column, Integer, String, create_engine, inspect
- from sqlalchemy.orm import Session, attributes
- from sqlalchemy.ext.declarative import declarative_base
-
- Base = declarative_base()
-
-
- class A(Base):
- __tablename__ = "a"
- id = Column(Integer, primary_key=True)
- data = Column(String)
-
-
- e = create_engine("sqlite://", echo=True)
- Base.metadata.create_all(e)
-
- sess = Session(e)
-
- a1 = A(data="a1")
- sess.add(a1)
- sess.commit() # a1 is now expired
-
- # history doesn't emit loader callables
- assert inspect(a1).attrs.data.history == (None, None, None)
-
- # in 0.8, this would fail to load the unloaded state.
- assert attributes.get_history(a1, "data") == (
- (),
- [
- "a1",
- ],
- (),
- )
-
- # load_history() is now equivalent to get_history() with
- # passive=PASSIVE_OFF ^ INIT_OK
- assert inspect(a1).attrs.data.load_history() == (
- (),
- [
- "a1",
- ],
- (),
- )
-
-:ticket:`2787`
-
-.. _behavioral_changes_core_09:
-
-Behavioral Changes - Core
-=========================
-
-Type objects no longer accept ignored keyword arguments
--------------------------------------------------------
-
-Up through the 0.8 series, most type objects accepted arbitrary keyword
-arguments which were silently ignored::
-
- from sqlalchemy import Date, Integer
-
- # storage_format argument here has no effect on any backend;
- # it needs to be on the SQLite-specific type
- d = Date(storage_format="%(day)02d.%(month)02d.%(year)04d")
-
- # display_width argument here has no effect on any backend;
- # it needs to be on the MySQL-specific type
- i = Integer(display_width=5)
-
-This was a very old bug for which a deprecation warning was added to the
-0.8 series, but because nobody ever runs Python with the "-W" flag, it
-was mostly never seen:
-
-.. sourcecode:: text
-
- $ python -W always::DeprecationWarning ~/dev/sqlalchemy/test.py
- /Users/classic/dev/sqlalchemy/test.py:5: SADeprecationWarning: Passing arguments to
- type object constructor <class 'sqlalchemy.types.Date'> is deprecated
- d = Date(storage_format="%(day)02d.%(month)02d.%(year)04d")
- /Users/classic/dev/sqlalchemy/test.py:9: SADeprecationWarning: Passing arguments to
- type object constructor <class 'sqlalchemy.types.Integer'> is deprecated
- i = Integer(display_width=5)
-
-As of the 0.9 series the "catch all" constructor is removed from
-:class:`.TypeEngine`, and these meaningless arguments are no longer accepted.
-
-The correct way to make use of dialect-specific arguments such as
-``storage_format`` and ``display_width`` is to use the appropriate
-dialect-specific types::
-
- from sqlalchemy.dialects.sqlite import DATE
- from sqlalchemy.dialects.mysql import INTEGER
-
- d = DATE(storage_format="%(day)02d.%(month)02d.%(year)04d")
-
- i = INTEGER(display_width=5)
-
-What about the case where we want the dialect-agnostic type also? We
-use the :meth:`.TypeEngine.with_variant` method::
-
- from sqlalchemy import Date, Integer
- from sqlalchemy.dialects.sqlite import DATE
- from sqlalchemy.dialects.mysql import INTEGER
-
- d = Date().with_variant(
- DATE(storage_format="%(day)02d.%(month)02d.%(year)04d"), "sqlite"
- )
-
- i = Integer().with_variant(INTEGER(display_width=5), "mysql")
-
-:meth:`.TypeEngine.with_variant` isn't new, it was added in SQLAlchemy
-0.7.2. So code that is running on the 0.8 series can be corrected to use
-this approach and tested before upgrading to 0.9.
-
-``None`` can no longer be used as a "partial AND" constructor
---------------------------------------------------------------
-
-``None`` can no longer be used as the "backstop" to form an AND condition piecemeal.
-This pattern was not a documented pattern even though some SQLAlchemy internals
-made use of it::
-
- condition = None
-
- for cond in conditions:
- condition = condition & cond
-
- if condition is not None:
- stmt = stmt.where(condition)
-
-The above sequence, when ``conditions`` is non-empty, will on 0.9 produce
-``SELECT .. WHERE <condition> AND NULL``. The ``None`` is no longer implicitly
-ignored, and is instead consistent with when ``None`` is interpreted in other
-contexts besides that of a conjunction.
-
-The correct code for both 0.8 and 0.9 should read::
-
- from sqlalchemy.sql import and_
-
- if conditions:
- stmt = stmt.where(and_(*conditions))
-
-Another variant that works on all backends on 0.9, but on 0.8 only works on
-backends that support boolean constants::
-
- from sqlalchemy.sql import true
-
- condition = true()
-
- for cond in conditions:
- condition = cond & condition
-
- stmt = stmt.where(condition)
-
-On 0.8, this will produce a SELECT statement that always has ``AND true``
-in the WHERE clause, which is not accepted by backends that don't support
-boolean constants (MySQL, MSSQL). On 0.9, the ``true`` constant will be dropped
-within an ``and_()`` conjunction.
-
-.. seealso::
-
- :ref:`migration_2804`
-
-.. _migration_2873:
-
-The "password" portion of a ``create_engine()`` no longer considers the ``+`` sign as an encoded space
-------------------------------------------------------------------------------------------------------
-
-For whatever reason, the Python function ``unquote_plus()`` was applied to the
-"password" field of a URL, which is an incorrect application of the
-encoding rules described in `RFC 1738 <https://www.ietf.org/rfc/rfc1738.txt>`_
-in that it escaped spaces as plus signs. The stringification of a URL
-now only encodes ":", "@", or "/" and nothing else, and is now applied to both the
-``username`` and ``password`` fields (previously it only applied to the
-password). On parsing, encoded characters are converted, but plus signs and
-spaces are passed through as is:
-
-.. sourcecode:: text
-
- # password: "pass word + other:words"
- dbtype://user:pass word + other%3Awords@host/dbname
-
- # password: "apples/oranges"
- dbtype://username:apples%2Foranges@hostspec/database
-
- # password: "apples@oranges@@"
- dbtype://username:apples%40oranges%40%40@hostspec/database
-
- # password: '', username is "username@"
- dbtype://username%40:@hostspec/database
-
-
-:ticket:`2873`
-
-.. _migration_2879:
-
-The precedence rules for COLLATE have been changed
---------------------------------------------------
-
-Previously, an expression like the following::
-
- print((column("x") == "somevalue").collate("en_EN"))
-
-would produce an expression like this:
-
-.. sourcecode:: sql
-
- -- 0.8 behavior
- (x = :x_1) COLLATE en_EN
-
-The above is misunderstood by MSSQL and is generally not the syntax suggested
-for any database. The expression will now produce the syntax illustrated
-by that of most database documentation:
-
-.. sourcecode:: sql
-
- -- 0.9 behavior
- x = :x_1 COLLATE en_EN
-
-The potentially backwards incompatible change arises if the
-:meth:`.ColumnOperators.collate` operator is being applied to the right-hand
-column, as follows::
-
- print(column("x") == literal("somevalue").collate("en_EN"))
-
-In 0.8, this produces:
-
-.. sourcecode:: sql
-
- x = :param_1 COLLATE en_EN
-
-However in 0.9, will now produce the more accurate, but probably not what you
-want, form of:
-
-.. sourcecode:: sql
-
- x = (:param_1 COLLATE en_EN)
-
-The :meth:`.ColumnOperators.collate` operator now works more appropriately within an
-``ORDER BY`` expression as well, as a specific precedence has been given to the
-``ASC`` and ``DESC`` operators which will again ensure no parentheses are
-generated:
-
-.. sourcecode:: pycon+sql
-
- >>> # 0.8
- >>> print(column("x").collate("en_EN").desc())
- {printsql}(x COLLATE en_EN) DESC{stop}
-
- >>> # 0.9
- >>> print(column("x").collate("en_EN").desc())
- {printsql}x COLLATE en_EN DESC{stop}
-
-:ticket:`2879`
-
-
-
-.. _migration_2878:
-
-PostgreSQL CREATE TYPE <x> AS ENUM now applies quoting to values
-----------------------------------------------------------------
-
-The :class:`_postgresql.ENUM` type will now apply escaping to single quote
-signs within the enumerated values:
-
-.. sourcecode:: pycon+sql
-
- >>> from sqlalchemy.dialects import postgresql
- >>> type = postgresql.ENUM("one", "two", "three's", name="myenum")
- >>> from sqlalchemy.dialects.postgresql import base
- >>> print(base.CreateEnumType(type).compile(dialect=postgresql.dialect()))
- {printsql}CREATE TYPE myenum AS ENUM ('one','two','three''s')
-
-Existing workarounds which already escape single quote signs will need to be
-modified, else they will now double-escape.
-
-:ticket:`2878`
-
-New Features
-============
-
-.. _feature_2268:
-
-Event Removal API
------------------
-
-Events established using :func:`.event.listen` or :func:`.event.listens_for`
-can now be removed using the new :func:`.event.remove` function. The ``target``,
-``identifier`` and ``fn`` arguments sent to :func:`.event.remove` need to match
-exactly those which were sent for listening, and the event will be removed
-from all locations in which it had been established::
-
- @event.listens_for(MyClass, "before_insert", propagate=True)
- def my_before_insert(mapper, connection, target):
- """listen for before_insert"""
- # ...
-
-
- event.remove(MyClass, "before_insert", my_before_insert)
-
-In the example above, the ``propagate=True`` flag is set. This
-means ``my_before_insert()`` is established as a listener for ``MyClass``
-as well as all subclasses of ``MyClass``.
-The system tracks everywhere that the ``my_before_insert()``
-listener function had been placed as a result of this call and removes it as
-a result of calling :func:`.event.remove`.
-
-The removal system uses a registry to associate arguments passed to
-:func:`.event.listen` with collections of event listeners, which are in many
-cases wrapped versions of the original user-supplied function. This registry
-makes heavy use of weak references in order to allow all the contained contents,
-such as listener targets, to be garbage collected when they go out of scope.
-
-:ticket:`2268`
-
-.. _feature_1418:
-
-New Query Options API; ``load_only()`` option
----------------------------------------------
-
-The system of loader options such as :func:`_orm.joinedload`,
-:func:`_orm.subqueryload`, :func:`_orm.lazyload`, :func:`_orm.defer`, etc.
-all build upon a new system known as :class:`_orm.Load`. :class:`_orm.Load` provides
-a "method chained" (a.k.a. :term:`generative`) approach to loader options, so that
-instead of joining together long paths using dots or multiple attribute names,
-an explicit loader style is given for each path.
-
-While the new way is slightly more verbose, it is simpler to understand
-in that there is no ambiguity in what options are being applied to which paths;
-it simplifies the method signatures of the options and provides greater flexibility
-particularly for column-based options. The old systems are to remain functional
-indefinitely as well and all styles can be mixed.
-
-**Old Way**
-
-To set a certain style of loading along every link in a multi-element path, the ``_all()``
-option has to be used::
-
- query(User).options(joinedload_all("orders.items.keywords"))
-
-**New Way**
-
-Loader options are now chainable, so the same ``joinedload(x)`` method is applied
-equally to each link, without the need to keep straight between
-:func:`_orm.joinedload` and :func:`_orm.joinedload_all`::
-
- query(User).options(joinedload("orders").joinedload("items").joinedload("keywords"))
-
-**Old Way**
-
-Setting an option on path that is based on a subclass requires that all
-links in the path be spelled out as class bound attributes, since the
-:meth:`.PropComparator.of_type` method needs to be called::
-
- session.query(Company).options(
- subqueryload_all(Company.employees.of_type(Engineer), Engineer.machines)
- )
-
-**New Way**
-
-Only those elements in the path that actually need :meth:`.PropComparator.of_type`
-need to be set as a class-bound attribute, string-based names can be resumed
-afterwards::
-
- session.query(Company).options(
- subqueryload(Company.employees.of_type(Engineer)).subqueryload("machines")
- )
-
-**Old Way**
-
-Setting the loader option on the last link in a long path uses a syntax
-that looks a lot like it should be setting the option for all links in the
-path, causing confusion::
-
- query(User).options(subqueryload("orders.items.keywords"))
-
-**New Way**
-
-A path can now be spelled out using :func:`.defaultload` for entries in the
-path where the existing loader style should be unchanged. More verbose
-but the intent is clearer::
-
- query(User).options(defaultload("orders").defaultload("items").subqueryload("keywords"))
-
-The dotted style can still be taken advantage of, particularly in the case
-of skipping over several path elements::
-
- query(User).options(defaultload("orders.items").subqueryload("keywords"))
-
-**Old Way**
-
-The :func:`.defer` option on a path needed to be spelled out with the full
-path for each column::
-
- query(User).options(defer("orders.description"), defer("orders.isopen"))
-
-**New Way**
-
-A single :class:`_orm.Load` object that arrives at the target path can have
-:meth:`_orm.Load.defer` called upon it repeatedly::
-
- query(User).options(defaultload("orders").defer("description").defer("isopen"))
-
-The Load Class
-^^^^^^^^^^^^^^^
-
-The :class:`_orm.Load` class can be used directly to provide a "bound" target,
-especially when multiple parent entities are present::
-
- from sqlalchemy.orm import Load
-
- query(User, Address).options(Load(Address).joinedload("entries"))
-
-Load Only
-^^^^^^^^^
-
-A new option :func:`.load_only` achieves a "defer everything but" style of load,
-loading only the given columns and deferring the rest::
-
- from sqlalchemy.orm import load_only
-
- query(User).options(load_only("name", "fullname"))
-
- # specify explicit parent entity
- query(User, Address).options(Load(User).load_only("name", "fullname"))
-
- # specify path
- query(User).options(joinedload(User.addresses).load_only("email_address"))
-
-Class-specific Wildcards
-^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Using :class:`_orm.Load`, a wildcard may be used to set the loading for all
-relationships (or perhaps columns) on a given entity, without affecting any
-others::
-
- # lazyload all User relationships
- query(User).options(Load(User).lazyload("*"))
-
- # undefer all User columns
- query(User).options(Load(User).undefer("*"))
-
- # lazyload all Address relationships
- query(User).options(defaultload(User.addresses).lazyload("*"))
-
- # undefer all Address columns
- query(User).options(defaultload(User.addresses).undefer("*"))
-
-:ticket:`1418`
-
-
-.. _feature_2877:
-
-New ``text()`` Capabilities
----------------------------
-
-The :func:`_expression.text` construct gains new methods:
-
-* :meth:`_expression.TextClause.bindparams` allows bound parameter types and values
- to be set flexibly::
-
- # setup values
- stmt = text(
- "SELECT id, name FROM user WHERE name=:name AND timestamp=:timestamp"
- ).bindparams(name="ed", timestamp=datetime(2012, 11, 10, 15, 12, 35))
-
- # setup types and/or values
- stmt = (
- text("SELECT id, name FROM user WHERE name=:name AND timestamp=:timestamp")
- .bindparams(bindparam("name", value="ed"), bindparam("timestamp", type_=DateTime()))
- .bindparam(timestamp=datetime(2012, 11, 10, 15, 12, 35))
- )
-
-* :meth:`_expression.TextClause.columns` supersedes the ``typemap`` option
- of :func:`_expression.text`, returning a new construct :class:`.TextAsFrom`::
-
- # turn a text() into an alias(), with a .c. collection:
- stmt = text("SELECT id, name FROM user").columns(id=Integer, name=String)
- stmt = stmt.alias()
-
- stmt = select([addresses]).select_from(
- addresses.join(stmt), addresses.c.user_id == stmt.c.id
- )
-
-
- # or into a cte():
- stmt = text("SELECT id, name FROM user").columns(id=Integer, name=String)
- stmt = stmt.cte("x")
-
- stmt = select([addresses]).select_from(
- addresses.join(stmt), addresses.c.user_id == stmt.c.id
- )
-
-:ticket:`2877`
-
-.. _feature_722:
-
-INSERT from SELECT
-------------------
-
-After literally years of pointless procrastination this relatively minor
-syntactical feature has been added, and is also backported to 0.8.3,
-so technically isn't "new" in 0.9. A :func:`_expression.select` construct or other
-compatible construct can be passed to the new method :meth:`_expression.Insert.from_select`
-where it will be used to render an ``INSERT .. SELECT`` construct:
-
-.. sourcecode:: pycon+sql
-
- >>> from sqlalchemy.sql import table, column
- >>> t1 = table("t1", column("a"), column("b"))
- >>> t2 = table("t2", column("x"), column("y"))
- >>> print(t1.insert().from_select(["a", "b"], t2.select().where(t2.c.y == 5)))
- {printsql}INSERT INTO t1 (a, b) SELECT t2.x, t2.y
- FROM t2
- WHERE t2.y = :y_1
-
-The construct is smart enough to also accommodate ORM objects such as classes
-and :class:`_query.Query` objects::
-
- s = Session()
- q = s.query(User.id, User.name).filter_by(name="ed")
- ins = insert(Address).from_select((Address.id, Address.email_address), q)
-
-rendering:
-
-.. sourcecode:: sql
-
- INSERT INTO addresses (id, email_address)
- SELECT users.id AS users_id, users.name AS users_name
- FROM users WHERE users.name = :name_1
-
-:ticket:`722`
-
-.. _feature_github_42:
-
-New FOR UPDATE support on ``select()``, ``Query()``
----------------------------------------------------
-
-An attempt is made to simplify the specification of the ``FOR UPDATE``
-clause on ``SELECT`` statements made within Core and ORM, and support is added
-for the ``FOR UPDATE OF`` SQL supported by PostgreSQL and Oracle.
-
-Using the core :meth:`_expression.GenerativeSelect.with_for_update`, options like ``FOR SHARE`` and
-``NOWAIT`` can be specified individually, rather than linking to arbitrary
-string codes::
-
- stmt = select([table]).with_for_update(read=True, nowait=True, of=table)
-
-On Posgtresql the above statement might render like:
-
-.. sourcecode:: sql
-
- SELECT table.a, table.b FROM table FOR SHARE OF table NOWAIT
-
-The :class:`_query.Query` object gains a similar method :meth:`_query.Query.with_for_update`
-which behaves in the same way. This method supersedes the existing
-:meth:`_query.Query.with_lockmode` method, which translated ``FOR UPDATE`` clauses
-using a different system. At the moment, the "lockmode" string argument is still
-accepted by the :meth:`.Session.refresh` method.
-
-
-.. _feature_2867:
-
-Floating Point String-Conversion Precision Configurable for Native Floating Point Types
----------------------------------------------------------------------------------------
-
-The conversion which SQLAlchemy does whenever a DBAPI returns a Python
-floating point type which is to be converted into a Python ``Decimal()``
-necessarily involves an intermediary step which converts the floating point
-value to a string. The scale used for this string conversion was previously
-hardcoded to 10, and is now configurable. The setting is available on
-both the :class:`.Numeric` as well as the :class:`.Float`
-type, as well as all SQL- and dialect-specific descendant types, using the
-parameter ``decimal_return_scale``. If the type supports a ``.scale`` parameter,
-as is the case with :class:`.Numeric` and some float types such as
-:class:`.mysql.DOUBLE`, the value of ``.scale`` is used as the default
-for ``.decimal_return_scale`` if it is not otherwise specified. If both
-``.scale`` and ``.decimal_return_scale`` are absent, then the default of
-10 takes place. E.g.::
-
- from sqlalchemy.dialects.mysql import DOUBLE
- import decimal
-
- data = Table(
- "data",
- metadata,
- Column("double_value", mysql.DOUBLE(decimal_return_scale=12, asdecimal=True)),
- )
-
- conn.execute(
- data.insert(),
- double_value=45.768392065789,
- )
- result = conn.scalar(select([data.c.double_value]))
-
- # previously, this would typically be Decimal("45.7683920658"),
- # e.g. trimmed to 10 decimal places
-
- # now we get 12, as requested, as MySQL can support this
- # much precision for DOUBLE
- assert result == decimal.Decimal("45.768392065789")
-
-:ticket:`2867`
-
-
-.. _change_2824:
-
-Column Bundles for ORM queries
-------------------------------
-
-The :class:`.Bundle` allows for querying of sets of columns, which are then
-grouped into one name under the tuple returned by the query. The initial
-purposes of :class:`.Bundle` are 1. to allow "composite" ORM columns to be
-returned as a single value in a column-based result set, rather than expanding
-them out into individual columns and 2. to allow the creation of custom result-set
-constructs within the ORM, using ad-hoc columns and return types, without involving
-the more heavyweight mechanics of mapped classes.
-
-.. seealso::
-
- :ref:`migration_2824`
-
- :ref:`bundles`
-
-:ticket:`2824`
-
-
-Server Side Version Counting
------------------------------
-
-The versioning feature of the ORM (now also documented at :ref:`mapper_version_counter`)
-can now make use of server-side version counting schemes, such as those produced
-by triggers or database system columns, as well as conditional programmatic schemes outside
-of the version_id_counter function itself. By providing the value ``False``
-to the ``version_id_generator`` parameter, the ORM will use the already-set version
-identifier, or alternatively fetch the version identifier
-from each row at the same time the INSERT or UPDATE is emitted. When using a
-server-generated version identifier, it is strongly
-recommended that this feature be used only on a backend with strong RETURNING
-support (PostgreSQL, SQL Server; Oracle also supports RETURNING but the cx_oracle
-driver has only limited support), else the additional SELECT statements will
-add significant performance
-overhead. The example provided at :ref:`server_side_version_counter` illustrates
-the usage of the PostgreSQL ``xmin`` system column in order to integrate it with
-the ORM's versioning feature.
-
-.. seealso::
-
- :ref:`server_side_version_counter`
-
-:ticket:`2793`
-
-.. _feature_1535:
-
-``include_backrefs=False`` option for ``@validates``
-----------------------------------------------------
-
-The :func:`.validates` function now accepts an option ``include_backrefs=True``,
-which will bypass firing the validator for the case where the event initiated
-from a backref::
-
- from sqlalchemy import Column, Integer, ForeignKey
- from sqlalchemy.orm import relationship, validates
- from sqlalchemy.ext.declarative import declarative_base
-
- Base = declarative_base()
-
-
- class A(Base):
- __tablename__ = "a"
-
- id = Column(Integer, primary_key=True)
- bs = relationship("B", backref="a")
-
- @validates("bs")
- def validate_bs(self, key, item):
- print("A.bs validator")
- return item
-
-
- class B(Base):
- __tablename__ = "b"
-
- id = Column(Integer, primary_key=True)
- a_id = Column(Integer, ForeignKey("a.id"))
-
- @validates("a", include_backrefs=False)
- def validate_a(self, key, item):
- print("B.a validator")
- return item
-
-
- a1 = A()
- a1.bs.append(B()) # prints only "A.bs validator"
-
-:ticket:`1535`
-
-
-PostgreSQL JSON Type
---------------------
-
-The PostgreSQL dialect now features a :class:`_postgresql.JSON` type to
-complement the :class:`_postgresql.HSTORE` type.
-
-.. seealso::
-
- :class:`_postgresql.JSON`
-
-:ticket:`2581`
-
-.. _feature_automap:
-
-Automap Extension
------------------
-
-A new extension is added in **0.9.1** known as :mod:`sqlalchemy.ext.automap`. This is an
-**experimental** extension which expands upon the functionality of Declarative
-as well as the :class:`.DeferredReflection` class. Essentially, the extension
-provides a base class :class:`.AutomapBase` which automatically generates
-mapped classes and relationships between them based on given table metadata.
-
-The :class:`_schema.MetaData` in use normally might be produced via reflection, but
-there is no requirement that reflection is used. The most basic usage
-illustrates how :mod:`sqlalchemy.ext.automap` is able to deliver mapped
-classes, including relationships, based on a reflected schema::
-
- from sqlalchemy.ext.automap import automap_base
- from sqlalchemy.orm import Session
- from sqlalchemy import create_engine
-
- Base = automap_base()
-
- # engine, suppose it has two tables 'user' and 'address' set up
- engine = create_engine("sqlite:///mydatabase.db")
-
- # reflect the tables
- Base.prepare(engine, reflect=True)
-
- # mapped classes are now created with names matching that of the table
- # name.
- User = Base.classes.user
- Address = Base.classes.address
-
- session = Session(engine)
-
- # rudimentary relationships are produced
- session.add(Address(email_address="foo@bar.com", user=User(name="foo")))
- session.commit()
-
- # collection-based relationships are by default named "<classname>_collection"
- print(u1.address_collection)
-
-Beyond that, the :class:`.AutomapBase` class is a declarative base, and supports
-all the features that declarative does. The "automapping" feature can be used
-with an existing, explicitly declared schema to generate relationships and
-missing classes only. Naming schemes and relationship-production routines
-can be dropped in using callable functions.
-
-It is hoped that the :class:`.AutomapBase` system provides a quick
-and modernized solution to the problem that the very famous
-`SQLSoup <https://sqlsoup.readthedocs.io/en/latest/>`_
-also tries to solve, that of generating a quick and rudimentary object
-model from an existing database on the fly. By addressing the issue strictly
-at the mapper configuration level, and integrating fully with existing
-Declarative class techniques, :class:`.AutomapBase` seeks to provide
-a well-integrated approach to the issue of expediently auto-generating ad-hoc
-mappings.
-
-.. seealso::
-
- :ref:`automap_toplevel`
-
-Behavioral Improvements
-=======================
-
-Improvements that should produce no compatibility issues except in exceedingly
-rare and unusual hypothetical cases, but are good to be aware of in case there are
-unexpected issues.
-
-.. _feature_joins_09:
-
-Many JOIN and LEFT OUTER JOIN expressions will no longer be wrapped in (SELECT * FROM ..) AS ANON_1
----------------------------------------------------------------------------------------------------
-
-For many years, the SQLAlchemy ORM has been held back from being able to nest
-a JOIN inside the right side of an existing JOIN (typically a LEFT OUTER JOIN,
-as INNER JOINs could always be flattened):
-
-.. sourcecode:: sql
-
- SELECT a.*, b.*, c.* FROM a LEFT OUTER JOIN (b JOIN c ON b.id = c.id) ON a.id
-
-This was due to the fact that SQLite up until version **3.7.16** cannot parse a statement of the above format:
-
-.. sourcecode:: text
-
- SQLite version 3.7.15.2 2013-01-09 11:53:05
- Enter ".help" for instructions
- Enter SQL statements terminated with a ";"
- sqlite> create table a(id integer);
- sqlite> create table b(id integer);
- sqlite> create table c(id integer);
- sqlite> select a.id, b.id, c.id from a left outer join (b join c on b.id=c.id) on b.id=a.id;
- Error: no such column: b.id
-
-Right-outer-joins are of course another way to work around right-side
-parenthesization; this would be significantly complicated and visually unpleasant
-to implement, but fortunately SQLite doesn't support RIGHT OUTER JOIN either :):
-
-.. sourcecode:: sql
-
- sqlite> select a.id, b.id, c.id from b join c on b.id=c.id
- ...> right outer join a on b.id=a.id;
- Error: RIGHT and FULL OUTER JOINs are not currently supported
-
-Back in 2005, it wasn't clear if other databases had trouble with this form,
-but today it seems clear every database tested except SQLite now supports it
-(Oracle 8, a very old database, doesn't support the JOIN keyword at all,
-but SQLAlchemy has always had a simple rewriting scheme in place for Oracle's syntax).
-To make matters worse, SQLAlchemy's usual workaround of applying a
-SELECT often degrades performance on platforms like PostgreSQL and MySQL:
-
-.. sourcecode:: sql
-
- SELECT a.*, anon_1.* FROM a LEFT OUTER JOIN (
- SELECT b.id AS b_id, c.id AS c_id
- FROM b JOIN c ON b.id = c.id
- ) AS anon_1 ON a.id=anon_1.b_id
-
-A JOIN like the above form is commonplace when working with joined-table inheritance structures;
-any time :meth:`_query.Query.join` is used to join from some parent to a joined-table subclass, or
-when :func:`_orm.joinedload` is used similarly, SQLAlchemy's ORM would always make sure a nested
-JOIN was never rendered, lest the query wouldn't be able to run on SQLite. Even though
-the Core has always supported a JOIN of the more compact form, the ORM had to avoid it.
-
-An additional issue would arise when producing joins across many-to-many relationships
-where special criteria is present in the ON clause. Consider an eager load join like the following::
-
- session.query(Order).outerjoin(Order.items)
-
-Assuming a many-to-many from ``Order`` to ``Item`` which actually refers to a subclass
-like ``Subitem``, the SQL for the above would look like:
-
-.. sourcecode:: sql
-
- SELECT order.id, order.name
- FROM order LEFT OUTER JOIN order_item ON order.id = order_item.order_id
- LEFT OUTER JOIN item ON order_item.item_id = item.id AND item.type = 'subitem'
-
-What's wrong with the above query? Basically, that it will load many ``order`` /
-``order_item`` rows where the criteria of ``item.type == 'subitem'`` is not true.
-
-As of SQLAlchemy 0.9, an entirely new approach has been taken. The ORM no longer
-worries about nesting JOINs in the right side of an enclosing JOIN, and it now will
-render these as often as possible while still returning the correct results. When
-the SQL statement is passed to be compiled, the **dialect compiler** will **rewrite the join**
-to suit the target backend, if that backend is known to not support a right-nested
-JOIN (which currently is only SQLite - if other backends have this issue please
-let us know!).
-
-So a regular ``query(Parent).join(Subclass)`` will now usually produce a simpler
-expression:
-
-.. sourcecode:: sql
-
- SELECT parent.id AS parent_id
- FROM parent JOIN (
- base_table JOIN subclass_table
- ON base_table.id = subclass_table.id) ON parent.id = base_table.parent_id
-
-Joined eager loads like ``query(Parent).options(joinedload(Parent.subclasses))``
-will alias the individual tables instead of wrapping in an ``ANON_1``:
-
-.. sourcecode:: sql
-
- SELECT parent.*, base_table_1.*, subclass_table_1.* FROM parent
- LEFT OUTER JOIN (
- base_table AS base_table_1 JOIN subclass_table AS subclass_table_1
- ON base_table_1.id = subclass_table_1.id)
- ON parent.id = base_table_1.parent_id
-
-Many-to-many joins and eagerloads will right nest the "secondary" and "right" tables:
-
-.. sourcecode:: sql
-
- SELECT order.id, order.name
- FROM order LEFT OUTER JOIN
- (order_item JOIN item ON order_item.item_id = item.id AND item.type = 'subitem')
- ON order_item.order_id = order.id
-
-All of these joins, when rendered with a :class:`_expression.Select` statement that specifically
-specifies ``use_labels=True``, which is true for all the queries the ORM emits,
-are candidates for "join rewriting", which is the process of rewriting all those right-nested
-joins into nested SELECT statements, while maintaining the identical labeling used by
-the :class:`_expression.Select`. So SQLite, the one database that won't support this very
-common SQL syntax even in 2013, shoulders the extra complexity itself,
-with the above queries rewritten as:
-
-.. sourcecode:: sql
-
- -- sqlite only!
- SELECT parent.id AS parent_id
- FROM parent JOIN (
- SELECT base_table.id AS base_table_id,
- base_table.parent_id AS base_table_parent_id,
- subclass_table.id AS subclass_table_id
- FROM base_table JOIN subclass_table ON base_table.id = subclass_table.id
- ) AS anon_1 ON parent.id = anon_1.base_table_parent_id
-
- -- sqlite only!
- SELECT parent.id AS parent_id, anon_1.subclass_table_1_id AS subclass_table_1_id,
- anon_1.base_table_1_id AS base_table_1_id,
- anon_1.base_table_1_parent_id AS base_table_1_parent_id
- FROM parent LEFT OUTER JOIN (
- SELECT base_table_1.id AS base_table_1_id,
- base_table_1.parent_id AS base_table_1_parent_id,
- subclass_table_1.id AS subclass_table_1_id
- FROM base_table AS base_table_1
- JOIN subclass_table AS subclass_table_1 ON base_table_1.id = subclass_table_1.id
- ) AS anon_1 ON parent.id = anon_1.base_table_1_parent_id
-
- -- sqlite only!
- SELECT "order".id AS order_id
- FROM "order" LEFT OUTER JOIN (
- SELECT order_item_1.order_id AS order_item_1_order_id,
- order_item_1.item_id AS order_item_1_item_id,
- item.id AS item_id, item.type AS item_type
- FROM order_item AS order_item_1
- JOIN item ON item.id = order_item_1.item_id AND item.type IN (?)
- ) AS anon_1 ON "order".id = anon_1.order_item_1_order_id
-
-.. note::
-
- As of SQLAlchemy 1.1, the workarounds present in this feature for SQLite
- will automatically disable themselves when SQLite version **3.7.16**
- or greater is detected, as SQLite has repaired support for right-nested joins.
-
-The :meth:`_expression.Join.alias`, :func:`.aliased` and :func:`.with_polymorphic` functions now
-support a new argument, ``flat=True``, which is used to construct aliases of joined-table
-entities without embedding into a SELECT. This flag is not on by default, to help with
-backwards compatibility - but now a "polymorphic" selectable can be joined as a target
-without any subqueries generated::
-
- employee_alias = with_polymorphic(Person, [Engineer, Manager], flat=True)
-
- session.query(Company).join(Company.employees.of_type(employee_alias)).filter(
- or_(Engineer.primary_language == "python", Manager.manager_name == "dilbert")
- )
-
-Generates (everywhere except SQLite):
-
-.. sourcecode:: sql
-
- SELECT companies.company_id AS companies_company_id, companies.name AS companies_name
- FROM companies JOIN (
- people AS people_1
- LEFT OUTER JOIN engineers AS engineers_1 ON people_1.person_id = engineers_1.person_id
- LEFT OUTER JOIN managers AS managers_1 ON people_1.person_id = managers_1.person_id
- ) ON companies.company_id = people_1.company_id
- WHERE engineers.primary_language = %(primary_language_1)s
- OR managers.manager_name = %(manager_name_1)s
-
-:ticket:`2369` :ticket:`2587`
-
-.. _feature_2976:
-
-Right-nested inner joins available in joined eager loads
----------------------------------------------------------
-
-As of version 0.9.4, the above mentioned right-nested joining can be enabled
-in the case of a joined eager load where an "outer" join is linked to an "inner"
-on the right side.
-
-Normally, a joined eager load chain like the following::
-
- query(User).options(
- joinedload("orders", innerjoin=False).joinedload("items", innerjoin=True)
- )
-
-Would not produce an inner join; because of the LEFT OUTER JOIN from user->order,
-joined eager loading could not use an INNER join from order->items without changing
-the user rows that are returned, and would instead ignore the "chained" ``innerjoin=True``
-directive. How 0.9.0 should have delivered this would be that instead of:
-
-.. sourcecode:: sql
-
- FROM users LEFT OUTER JOIN orders ON <onclause> LEFT OUTER JOIN items ON <onclause>
-
-the new "right-nested joins are OK" logic would kick in, and we'd get:
-
-.. sourcecode:: sql
-
- FROM users LEFT OUTER JOIN (orders JOIN items ON <onclause>) ON <onclause>
-
-Since we missed the boat on that, to avoid further regressions we've added the above
-functionality by specifying the string ``"nested"`` to :paramref:`_orm.joinedload.innerjoin`::
-
- query(User).options(
- joinedload("orders", innerjoin=False).joinedload("items", innerjoin="nested")
- )
-
-This feature is new in 0.9.4.
-
-:ticket:`2976`
-
-
-
-ORM can efficiently fetch just-generated INSERT/UPDATE defaults using RETURNING
--------------------------------------------------------------------------------
-
-The :class:`_orm.Mapper` has long supported an undocumented flag known as
-``eager_defaults=True``. The effect of this flag is that when an INSERT or UPDATE
-proceeds, and the row is known to have server-generated default values,
-a SELECT would immediately follow it in order to "eagerly" load those new values.
-Normally, the server-generated columns are marked as "expired" on the object,
-so that no overhead is incurred unless the application actually accesses these
-columns soon after the flush. The ``eager_defaults`` flag was therefore not
-of much use as it could only decrease performance, and was present only to support
-exotic event schemes where users needed default values to be available
-immediately within the flush process.
-
-In 0.9, as a result of the version id enhancements, ``eager_defaults`` can now
-emit a RETURNING clause for these values, so on a backend with strong RETURNING
-support in particular PostgreSQL, the ORM can fetch newly generated default
-and SQL expression values inline with the INSERT or UPDATE. ``eager_defaults``,
-when enabled, makes use of RETURNING automatically when the target backend
-and :class:`_schema.Table` supports "implicit returning".
-
-.. _change_2836:
-
-Subquery Eager Loading will apply DISTINCT to the innermost SELECT for some queries
-------------------------------------------------------------------------------------
-
-In an effort to reduce the number of duplicate rows that can be generated
-by subquery eager loading when a many-to-one relationship is involved, a
-DISTINCT keyword will be applied to the innermost SELECT when the join is
-targeting columns that do not comprise the primary key, as in when loading
-along a many to one.
-
-That is, when subquery loading on a many-to-one from A->B:
-
-.. sourcecode:: sql
-
- SELECT b.id AS b_id, b.name AS b_name, anon_1.b_id AS a_b_id
- FROM (SELECT DISTINCT a_b_id FROM a) AS anon_1
- JOIN b ON b.id = anon_1.a_b_id
-
-Since ``a.b_id`` is a non-distinct foreign key, DISTINCT is applied so that
-redundant ``a.b_id`` are eliminated. The behavior can be turned on or off
-unconditionally for a particular :func:`_orm.relationship` using the flag
-``distinct_target_key``, setting the value to ``True`` for unconditionally
-on, ``False`` for unconditionally off, and ``None`` for the feature to take
-effect when the target SELECT is against columns that do not comprise a full
-primary key. In 0.9, ``None`` is the default.
-
-The option is also backported to 0.8 where the ``distinct_target_key``
-option defaults to ``False``.
-
-While the feature here is designed to help performance by eliminating
-duplicate rows, the ``DISTINCT`` keyword in SQL itself can have a negative
-performance impact. If columns in the SELECT are not indexed, ``DISTINCT``
-will likely perform an ``ORDER BY`` on the rowset which can be expensive.
-By keeping the feature limited just to foreign keys which are hopefully
-indexed in any case, it's expected that the new defaults are reasonable.
-
-The feature also does not eliminate every possible dupe-row scenario; if
-a many-to-one is present elsewhere in the chain of joins, dupe rows may still
-be present.
-
-:ticket:`2836`
-
-.. _migration_2789:
-
-Backref handlers can now propagate more than one level deep
------------------------------------------------------------
-
-The mechanism by which attribute events pass along their "initiator", that is
-the object associated with the start of the event, has been changed; instead
-of a :class:`.AttributeImpl` being passed, a new object :class:`.attributes.Event`
-is passed instead; this object refers to the :class:`.AttributeImpl` as well as
-to an "operation token", representing if the operation is an append, remove,
-or replace operation.
-
-The attribute event system no longer looks at this "initiator" object in order to halt a
-recursive series of attribute events. Instead, the system of preventing endless
-recursion due to mutually-dependent backref handlers has been moved
-to the ORM backref event handlers specifically, which now take over the role
-of ensuring that a chain of mutually-dependent events (such as append to collection
-A.bs, set many-to-one attribute B.a in response) doesn't go into an endless recursion
-stream. The rationale here is that the backref system, given more detail and control
-over event propagation, can finally allow operations more than one level deep
-to occur; the typical scenario is when a collection append results in a many-to-one
-replacement operation, which in turn should cause the item to be removed from a
-previous collection::
-
- class Parent(Base):
- __tablename__ = "parent"
-
- id = Column(Integer, primary_key=True)
- children = relationship("Child", backref="parent")
-
-
- class Child(Base):
- __tablename__ = "child"
-
- id = Column(Integer, primary_key=True)
- parent_id = Column(ForeignKey("parent.id"))
-
-
- p1 = Parent()
- p2 = Parent()
- c1 = Child()
-
- p1.children.append(c1)
-
- assert c1.parent is p1 # backref event establishes c1.parent as p1
-
- p2.children.append(c1)
-
- assert c1.parent is p2 # backref event establishes c1.parent as p2
- assert c1 not in p1.children # second backref event removes c1 from p1.children
-
-Above, prior to this change, the ``c1`` object would still have been present
-in ``p1.children``, even though it is also present in ``p2.children`` at the
-same time; the backref handlers would have stopped at replacing ``c1.parent`` with
-``p2`` instead of ``p1``. In 0.9, using the more detailed :class:`.Event`
-object as well as letting the backref handlers make more detailed decisions about
-these objects, the propagation can continue onto removing ``c1`` from ``p1.children``
-while maintaining a check against the propagation from going into an endless
-recursive loop.
-
-End-user code which a. makes use of the :meth:`.AttributeEvents.set`,
-:meth:`.AttributeEvents.append`, or :meth:`.AttributeEvents.remove` events,
-and b. initiates further attribute modification operations as a result of these
-events may need to be modified to prevent recursive loops, as the attribute system
-no longer stops a chain of events from propagating endlessly in the absence of the backref
-event handlers. Additionally, code which depends upon the value of the ``initiator``
-will need to be adjusted to the new API, and furthermore must be ready for the
-value of ``initiator`` to change from its original value within a string of
-backref-initiated events, as the backref handlers may now swap in a
-new ``initiator`` value for some operations.
-
-:ticket:`2789`
-
-.. _change_2838:
-
-The typing system now handles the task of rendering "literal bind" values
--------------------------------------------------------------------------
-
-A new method is added to :class:`.TypeEngine` :meth:`.TypeEngine.literal_processor`
-as well as :meth:`.TypeDecorator.process_literal_param` for :class:`.TypeDecorator`
-which take on the task of rendering so-called "inline literal parameters" - parameters
-that normally render as "bound" values, but are instead being rendered inline
-into the SQL statement due to the compiler configuration. This feature is used
-when generating DDL for constructs such as :class:`.CheckConstraint`, as well
-as by Alembic when using constructs such as ``op.inline_literal()``. Previously,
-a simple "isinstance" check checked for a few basic types, and the "bind processor"
-was used unconditionally, leading to such issues as strings being encoded into utf-8
-prematurely.
-
-Custom types written with :class:`.TypeDecorator` should continue to work in
-"inline literal" scenarios, as the :meth:`.TypeDecorator.process_literal_param`
-falls back to :meth:`.TypeDecorator.process_bind_param` by default, as these methods
-usually handle a data manipulation, not as much how the data is presented to the
-database. :meth:`.TypeDecorator.process_literal_param` can be specified to
-specifically produce a string representing how a value should be rendered
-into an inline DDL statement.
-
-:ticket:`2838`
-
-
-.. _change_2812:
-
-Schema identifiers now carry along their own quoting information
----------------------------------------------------------------------
-
-This change simplifies the Core's usage of so-called "quote" flags, such
-as the ``quote`` flag passed to :class:`_schema.Table` and :class:`_schema.Column`. The flag
-is now internalized within the string name itself, which is now represented
-as an instance of :class:`.quoted_name`, a string subclass. The
-:class:`.IdentifierPreparer` now relies solely on the quoting preferences
-reported by the :class:`.quoted_name` object rather than checking for any
-explicit ``quote`` flags in most cases. The issue resolved here includes
-that various case-sensitive methods such as :meth:`_engine.Engine.has_table` as well
-as similar methods within dialects now function with explicitly quoted names,
-without the need to complicate or introduce backwards-incompatible changes
-to those APIs (many of which are 3rd party) with the details of quoting flags -
-in particular, a wider range of identifiers now function correctly with the
-so-called "uppercase" backends like Oracle, Firebird, and DB2 (backends that
-store and report upon table and column names using all uppercase for case
-insensitive names).
-
-The :class:`.quoted_name` object is used internally as needed; however if
-other keywords require fixed quoting preferences, the class is available
-publicly.
-
-:ticket:`2812`
-
-.. _migration_2804:
-
-Improved rendering of Boolean constants, NULL constants, conjunctions
-----------------------------------------------------------------------
-
-New capabilities have been added to the :func:`.true` and :func:`.false`
-constants, in particular in conjunction with :func:`.and_` and :func:`.or_`
-functions as well as the behavior of the WHERE/HAVING clauses in conjunction
-with these types, boolean types overall, and the :func:`.null` constant.
-
-Starting with a table such as this::
-
- from sqlalchemy import Table, Boolean, Integer, Column, MetaData
-
- t1 = Table("t", MetaData(), Column("x", Boolean()), Column("y", Integer))
-
-A select construct will now render the boolean column as a binary expression
-on backends that don't feature ``true``/``false`` constant behavior:
-
-.. sourcecode:: pycon+sql
-
- >>> from sqlalchemy import select, and_, false, true
- >>> from sqlalchemy.dialects import mysql, postgresql
-
- >>> print(select([t1]).where(t1.c.x).compile(dialect=mysql.dialect()))
- {printsql}SELECT t.x, t.y FROM t WHERE t.x = 1
-
-The :func:`.and_` and :func:`.or_` constructs will now exhibit quasi
-"short circuit" behavior, that is truncating a rendered expression, when a
-:func:`.true` or :func:`.false` constant is present:
-
-.. sourcecode:: pycon+sql
-
- >>> print(
- ... select([t1]).where(and_(t1.c.y > 5, false())).compile(dialect=postgresql.dialect())
- ... )
- {printsql}SELECT t.x, t.y FROM t WHERE false
-
-:func:`.true` can be used as the base to build up an expression:
-
-.. sourcecode:: pycon+sql
-
- >>> expr = true()
- >>> expr = expr & (t1.c.y > 5)
- >>> print(select([t1]).where(expr))
- {printsql}SELECT t.x, t.y FROM t WHERE t.y > :y_1
-
-The boolean constants :func:`.true` and :func:`.false` themselves render as
-``0 = 1`` and ``1 = 1`` for a backend with no boolean constants:
-
-.. sourcecode:: pycon+sql
-
- >>> print(select([t1]).where(and_(t1.c.y > 5, false())).compile(dialect=mysql.dialect()))
- {printsql}SELECT t.x, t.y FROM t WHERE 0 = 1
-
-Interpretation of ``None``, while not particularly valid SQL, is at least
-now consistent:
-
-.. sourcecode:: pycon+sql
-
- >>> print(select([t1.c.x]).where(None))
- {printsql}SELECT t.x FROM t WHERE NULL{stop}
-
- >>> print(select([t1.c.x]).where(None).where(None))
- {printsql}SELECT t.x FROM t WHERE NULL AND NULL{stop}
-
- >>> print(select([t1.c.x]).where(and_(None, None)))
- {printsql}SELECT t.x FROM t WHERE NULL AND NULL{stop}
-
-:ticket:`2804`
-
-.. _migration_1068:
-
-Label constructs can now render as their name alone in an ORDER BY
-------------------------------------------------------------------
-
-For the case where a :class:`.Label` is used in both the columns clause
-as well as the ORDER BY clause of a SELECT, the label will render as
-just its name in the ORDER BY clause, assuming the underlying dialect
-reports support of this feature.
-
-E.g. an example like::
-
- from sqlalchemy.sql import table, column, select, func
-
- t = table("t", column("c1"), column("c2"))
- expr = (func.foo(t.c.c1) + t.c.c2).label("expr")
-
- stmt = select([expr]).order_by(expr)
-
- print(stmt)
-
-Prior to 0.9 would render as:
-
-.. sourcecode:: sql
-
- SELECT foo(t.c1) + t.c2 AS expr
- FROM t ORDER BY foo(t.c1) + t.c2
-
-And now renders as:
-
-.. sourcecode:: sql
-
- SELECT foo(t.c1) + t.c2 AS expr
- FROM t ORDER BY expr
-
-The ORDER BY only renders the label if the label isn't further
-embedded into an expression within the ORDER BY, other than a simple
-``ASC`` or ``DESC``.
-
-The above format works on all databases tested, but might have
-compatibility issues with older database versions (MySQL 4? Oracle 8?
-etc.). Based on user reports we can add rules that will disable the
-feature based on database version detection.
-
-:ticket:`1068`
-
-.. _migration_2848:
-
-``RowProxy`` now has tuple-sorting behavior
--------------------------------------------
-
-The :class:`.RowProxy` object acts much like a tuple, but up until now
-would not sort as a tuple if a list of them were sorted using ``sorted()``.
-The ``__eq__()`` method now compares both sides as a tuple and also
-an ``__lt__()`` method has been added::
-
- users.insert().execute(
- dict(user_id=1, user_name="foo"),
- dict(user_id=2, user_name="bar"),
- dict(user_id=3, user_name="def"),
- )
-
- rows = users.select().order_by(users.c.user_name).execute().fetchall()
-
- eq_(rows, [(2, "bar"), (3, "def"), (1, "foo")])
-
- eq_(sorted(rows), [(1, "foo"), (2, "bar"), (3, "def")])
-
-:ticket:`2848`
-
-.. _migration_2850:
-
-A bindparam() construct with no type gets upgraded via copy when a type is available
-------------------------------------------------------------------------------------
-
-The logic which "upgrades" a :func:`.bindparam` construct to take on the
-type of the enclosing expression has been improved in two ways. First, the
-:func:`.bindparam` object is **copied** before the new type is assigned, so that
-the given :func:`.bindparam` is not mutated in place. Secondly, this same
-operation occurs when an :class:`_expression.Insert` or :class:`_expression.Update` construct is compiled,
-regarding the "values" that were set in the statement via the :meth:`.ValuesBase.values`
-method.
-
-If given an untyped :func:`.bindparam`::
-
- bp = bindparam("some_col")
-
-If we use this parameter as follows::
-
- expr = mytable.c.col == bp
-
-The type for ``bp`` remains as ``NullType``, however if ``mytable.c.col``
-is of type ``String``, then ``expr.right``, that is the right side of the
-binary expression, will take on the ``String`` type. Previously, ``bp`` itself
-would have been changed in place to have ``String`` as its type.
-
-Similarly, this operation occurs in an :class:`_expression.Insert` or :class:`_expression.Update`::
-
- stmt = mytable.update().values(col=bp)
-
-Above, ``bp`` remains unchanged, but the ``String`` type will be used when
-the statement is executed, which we can see by examining the ``binds`` dictionary::
-
- >>> compiled = stmt.compile()
- >>> compiled.binds["some_col"].type
- String
-
-The feature allows custom types to take their expected effect within INSERT/UPDATE
-statements without needing to explicitly specify those types within every
-:func:`.bindparam` expression.
-
-The potentially backwards-compatible changes involve two unlikely
-scenarios. Since the bound parameter is
-**cloned**, users should not be relying upon making in-place changes to a
-:func:`.bindparam` construct once created. Additionally, code which uses
-:func:`.bindparam` within an :class:`_expression.Insert` or :class:`_expression.Update` statement
-which is relying on the fact that the :func:`.bindparam` is not typed according
-to the column being assigned towards will no longer function in that way.
-
-:ticket:`2850`
-
-
-.. _migration_1765:
-
-Columns can reliably get their type from a column referred to via ForeignKey
-----------------------------------------------------------------------------
-
-There's a long standing behavior which says that a :class:`_schema.Column` can be
-declared without a type, as long as that :class:`_schema.Column` is referred to
-by a :class:`_schema.ForeignKeyConstraint`, and the type from the referenced column
-will be copied into this one. The problem has been that this feature never
-worked very well and wasn't maintained. The core issue was that the
-:class:`_schema.ForeignKey` object doesn't know what target :class:`_schema.Column` it
-refers to until it is asked, typically the first time the foreign key is used
-to construct a :class:`_expression.Join`. So until that time, the parent :class:`_schema.Column`
-would not have a type, or more specifically, it would have a default type
-of :class:`.NullType`.
-
-While it's taken a long time, the work to reorganize the initialization of
-:class:`_schema.ForeignKey` objects has been completed such that this feature can
-finally work acceptably. At the core of the change is that the :attr:`_schema.ForeignKey.column`
-attribute no longer lazily initializes the location of the target :class:`_schema.Column`;
-the issue with this system was that the owning :class:`_schema.Column` would be stuck
-with :class:`.NullType` as its type until the :class:`_schema.ForeignKey` happened to
-be used.
-
-In the new version, the :class:`_schema.ForeignKey` coordinates with the eventual
-:class:`_schema.Column` it will refer to using internal attachment events, so that the
-moment the referencing :class:`_schema.Column` is associated with the
-:class:`_schema.MetaData`, all :class:`_schema.ForeignKey` objects that
-refer to it will be sent a message that they need to initialize their parent
-column. This system is more complicated but works more solidly; as a bonus,
-there are now tests in place for a wide variety of :class:`_schema.Column` /
-:class:`_schema.ForeignKey` configuration scenarios and error messages have been
-improved to be very specific to no less than seven different error conditions.
-
-Scenarios which now work correctly include:
-
-1. The type on a :class:`_schema.Column` is immediately present as soon as the
- target :class:`_schema.Column` becomes associated with the same :class:`_schema.MetaData`;
- this works no matter which side is configured first::
-
- >>> from sqlalchemy import Table, MetaData, Column, Integer, ForeignKey
- >>> metadata = MetaData()
- >>> t2 = Table("t2", metadata, Column("t1id", ForeignKey("t1.id")))
- >>> t2.c.t1id.type
- NullType()
- >>> t1 = Table("t1", metadata, Column("id", Integer, primary_key=True))
- >>> t2.c.t1id.type
- Integer()
-
-2. The system now works with :class:`_schema.ForeignKeyConstraint` as well::
-
- >>> from sqlalchemy import Table, MetaData, Column, Integer, ForeignKeyConstraint
- >>> metadata = MetaData()
- >>> t2 = Table(
- ... "t2",
- ... metadata,
- ... Column("t1a"),
- ... Column("t1b"),
- ... ForeignKeyConstraint(["t1a", "t1b"], ["t1.a", "t1.b"]),
- ... )
- >>> t2.c.t1a.type
- NullType()
- >>> t2.c.t1b.type
- NullType()
- >>> t1 = Table(
- ... "t1",
- ... metadata,
- ... Column("a", Integer, primary_key=True),
- ... Column("b", Integer, primary_key=True),
- ... )
- >>> t2.c.t1a.type
- Integer()
- >>> t2.c.t1b.type
- Integer()
-
-3. It even works for "multiple hops" - that is, a :class:`_schema.ForeignKey` that refers to a
- :class:`_schema.Column` that refers to another :class:`_schema.Column`::
-
- >>> from sqlalchemy import Table, MetaData, Column, Integer, ForeignKey
- >>> metadata = MetaData()
- >>> t2 = Table("t2", metadata, Column("t1id", ForeignKey("t1.id")))
- >>> t3 = Table("t3", metadata, Column("t2t1id", ForeignKey("t2.t1id")))
- >>> t2.c.t1id.type
- NullType()
- >>> t3.c.t2t1id.type
- NullType()
- >>> t1 = Table("t1", metadata, Column("id", Integer, primary_key=True))
- >>> t2.c.t1id.type
- Integer()
- >>> t3.c.t2t1id.type
- Integer()
-
-:ticket:`1765`
-
-
-Dialect Changes
-===============
-
-Firebird ``fdb`` is now the default Firebird dialect.
------------------------------------------------------
-
-The ``fdb`` dialect is now used if an engine is created without a dialect
-specifier, i.e. ``firebird://``. ``fdb`` is a ``kinterbasdb`` compatible
-DBAPI which per the Firebird project is now their official Python driver.
-
-:ticket:`2504`
-
-Firebird ``fdb`` and ``kinterbasdb`` set ``retaining=False`` by default
------------------------------------------------------------------------
-
-Both the ``fdb`` and ``kinterbasdb`` DBAPIs support a flag ``retaining=True``
-which can be passed to the ``commit()`` and ``rollback()`` methods of its
-connection. The documented rationale for this flag is so that the DBAPI
-can re-use internal transaction state for subsequent transactions, for the
-purposes of improving performance. However, newer documentation refers
-to analyses of Firebird's "garbage collection" which expresses that this flag
-can have a negative effect on the database's ability to process cleanup
-tasks, and has been reported as *lowering* performance as a result.
-
-It's not clear how this flag is actually usable given this information,
-and as it appears to be only a performance enhancing feature, it now defaults
-to ``False``. The value can be controlled by passing the flag ``retaining=True``
-to the :func:`_sa.create_engine` call. This is a new flag which is added as of
-0.8.2, so applications on 0.8.2 can begin setting this to ``True`` or ``False``
-as desired.
-
-.. seealso::
-
- :mod:`sqlalchemy.dialects.firebird.fdb`
-
- :mod:`sqlalchemy.dialects.firebird.kinterbasdb`
-
- https://pythonhosted.org/fdb/usage-guide.html#retaining-transactions - information
- on the "retaining" flag.
-
-:ticket:`2763`
-
-
-
-
-
+++ /dev/null
-=============================
-What's New in SQLAlchemy 1.0?
-=============================
-
-.. admonition:: About this Document
-
- This document describes changes between SQLAlchemy version 0.9,
- undergoing maintenance releases as of May, 2014,
- and SQLAlchemy version 1.0, released in April, 2015.
-
- Document last updated: June 9, 2015
-
-Introduction
-============
-
-This guide introduces what's new in SQLAlchemy version 1.0,
-and also documents changes which affect users migrating
-their applications from the 0.9 series of SQLAlchemy to 1.0.
-
-Please carefully review the sections on behavioral changes for
-potentially backwards-incompatible changes in behavior.
-
-
-New Features and Improvements - ORM
-===================================
-
-New Session Bulk INSERT/UPDATE API
-----------------------------------
-
-A new series of :class:`.Session` methods which provide hooks directly
-into the unit of work's facility for emitting INSERT and UPDATE
-statements has been created. When used correctly, this expert-oriented system
-can allow ORM-mappings to be used to generate bulk insert and update
-statements batched into executemany groups, allowing the statements
-to proceed at speeds that rival direct use of the Core.
-
-.. seealso::
-
- :ref:`bulk_operations` - introduction and full documentation
-
-:ticket:`3100`
-
-New Performance Example Suite
------------------------------
-
-Inspired by the benchmarking done for the :ref:`bulk_operations` feature
-as well as for the :ref:`faq_how_to_profile` section of the FAQ, a new
-example section has been added which features several scripts designed
-to illustrate the relative performance profile of various Core and ORM
-techniques. The scripts are organized into use cases, and are packaged
-under a single console interface such that any combination of demonstrations
-can be run, dumping out timings, Python profile results and/or RunSnake profile
-displays.
-
-.. seealso::
-
- :ref:`examples_performance`
-
-"Baked" Queries
----------------
-
-The "baked" query feature is an unusual new approach which allows for
-straightforward construction an invocation of :class:`_query.Query` objects
-using caching, which upon successive calls features vastly reduced
-Python function call overhead (over 75%). By specifying a
-:class:`_query.Query` object as a series of lambdas which are only invoked
-once, a query as a pre-compiled unit begins to be feasible::
-
- from sqlalchemy.ext import baked
- from sqlalchemy import bindparam
-
- bakery = baked.bakery()
-
-
- def search_for_user(session, username, email=None):
-
- baked_query = bakery(lambda session: session.query(User))
- baked_query += lambda q: q.filter(User.name == bindparam("username"))
-
- baked_query += lambda q: q.order_by(User.id)
-
- if email:
- baked_query += lambda q: q.filter(User.email == bindparam("email"))
-
- result = baked_query(session).params(username=username, email=email).all()
-
- return result
-
-.. seealso::
-
- :ref:`baked_toplevel`
-
-:ticket:`3054`
-
-.. _feature_3150:
-
-Improvements to declarative mixins, ``@declared_attr`` and related features
----------------------------------------------------------------------------
-
-The declarative system in conjunction with :class:`.declared_attr` has been
-overhauled to support new capabilities.
-
-A function decorated with :class:`.declared_attr` is now called only **after**
-any mixin-based column copies are generated. This means the function can
-call upon mixin-established columns and will receive a reference to the correct
-:class:`_schema.Column` object::
-
- class HasFooBar(object):
- foobar = Column(Integer)
-
- @declared_attr
- def foobar_prop(cls):
- return column_property("foobar: " + cls.foobar)
-
-
- class SomeClass(HasFooBar, Base):
- __tablename__ = "some_table"
- id = Column(Integer, primary_key=True)
-
-Above, ``SomeClass.foobar_prop`` will be invoked against ``SomeClass``,
-and ``SomeClass.foobar`` will be the final :class:`_schema.Column` object that is
-to be mapped to ``SomeClass``, as opposed to the non-copied object present
-directly on ``HasFooBar``, even though the columns aren't mapped yet.
-
-The :class:`.declared_attr` function now **memoizes** the value
-that's returned on a per-class basis, so that repeated calls to the same
-attribute will return the same value. We can alter the example to illustrate
-this::
-
- class HasFooBar(object):
- @declared_attr
- def foobar(cls):
- return Column(Integer)
-
- @declared_attr
- def foobar_prop(cls):
- return column_property("foobar: " + cls.foobar)
-
-
- class SomeClass(HasFooBar, Base):
- __tablename__ = "some_table"
- id = Column(Integer, primary_key=True)
-
-Previously, ``SomeClass`` would be mapped with one particular copy of
-the ``foobar`` column, but the ``foobar_prop`` by calling upon ``foobar``
-a second time would produce a different column. The value of
-``SomeClass.foobar`` is now memoized during declarative setup time, so that
-even before the attribute is mapped by the mapper, the interim column
-value will remain consistent no matter how many times the
-:class:`.declared_attr` is called upon.
-
-The two behaviors above should help considerably with declarative definition
-of many types of mapper properties that derive from other attributes, where
-the :class:`.declared_attr` function is called upon from other
-:class:`.declared_attr` functions locally present before the class is
-actually mapped.
-
-For a pretty slim edge case where one wishes to build a declarative mixin
-that establishes distinct columns per subclass, a new modifier
-:attr:`.declared_attr.cascading` is added. With this modifier, the
-decorated function will be invoked individually for each class in the
-mapped inheritance hierarchy. While this is already the behavior for
-special attributes such as ``__table_args__`` and ``__mapper_args__``,
-for columns and other properties the behavior by default assumes that attribute
-is affixed to the base class only, and just inherited from subclasses.
-With :attr:`.declared_attr.cascading`, individual behaviors can be
-applied::
-
- class HasIdMixin(object):
- @declared_attr.cascading
- def id(cls):
- if has_inherited_table(cls):
- return Column(ForeignKey("myclass.id"), primary_key=True)
- else:
- return Column(Integer, primary_key=True)
-
-
- class MyClass(HasIdMixin, Base):
- __tablename__ = "myclass"
- # ...
-
-
- class MySubClass(MyClass):
- """ """
-
- # ...
-
-.. seealso::
-
- :ref:`mixin_inheritance_columns`
-
-Finally, the :class:`.AbstractConcreteBase` class has been reworked
-so that a relationship or other mapper property can be set up inline
-on the abstract base::
-
- from sqlalchemy import Column, Integer, ForeignKey
- from sqlalchemy.orm import relationship
- from sqlalchemy.ext.declarative import (
- declarative_base,
- declared_attr,
- AbstractConcreteBase,
- )
-
- Base = declarative_base()
-
-
- class Something(Base):
- __tablename__ = "something"
- id = Column(Integer, primary_key=True)
-
-
- class Abstract(AbstractConcreteBase, Base):
- id = Column(Integer, primary_key=True)
-
- @declared_attr
- def something_id(cls):
- return Column(ForeignKey(Something.id))
-
- @declared_attr
- def something(cls):
- return relationship(Something)
-
-
- class Concrete(Abstract):
- __tablename__ = "cca"
- __mapper_args__ = {"polymorphic_identity": "cca", "concrete": True}
-
-The above mapping will set up a table ``cca`` with both an ``id`` and
-a ``something_id`` column, and ``Concrete`` will also have a relationship
-``something``. The new feature is that ``Abstract`` will also have an
-independently configured relationship ``something`` that builds against
-the polymorphic union of the base.
-
-:ticket:`3150` :ticket:`2670` :ticket:`3149` :ticket:`2952` :ticket:`3050`
-
-ORM full object fetches 25% faster
-----------------------------------
-
-The mechanics of the ``loading.py`` module as well as the identity map
-have undergone several passes of inlining, refactoring, and pruning, so
-that a raw load of rows now populates ORM-based objects around 25% faster.
-Assuming a 1M row table, a script like the following illustrates the type
-of load that's improved the most::
-
- import time
- from sqlalchemy import Integer, Column, create_engine, Table
- from sqlalchemy.orm import Session
- from sqlalchemy.ext.declarative import declarative_base
-
- Base = declarative_base()
-
-
- class Foo(Base):
- __table__ = Table(
- "foo",
- Base.metadata,
- Column("id", Integer, primary_key=True),
- Column("a", Integer(), nullable=False),
- Column("b", Integer(), nullable=False),
- Column("c", Integer(), nullable=False),
- )
-
-
- engine = create_engine("mysql+mysqldb://scott:tiger@localhost/test", echo=True)
-
- sess = Session(engine)
-
- now = time.time()
-
- # avoid using all() so that we don't have the overhead of building
- # a large list of full objects in memory
- for obj in sess.query(Foo).yield_per(100).limit(1000000):
- pass
-
- print("Total time: %d" % (time.time() - now))
-
-Local MacBookPro results bench from 19 seconds for 0.9 down to 14 seconds for
-1.0. The :meth:`_query.Query.yield_per` call is always a good idea when batching
-huge numbers of rows, as it prevents the Python interpreter from having
-to allocate a huge amount of memory for all objects and their instrumentation
-at once. Without the :meth:`_query.Query.yield_per`, the above script on the
-MacBookPro is 31 seconds on 0.9 and 26 seconds on 1.0, the extra time spent
-setting up very large memory buffers.
-
-.. _feature_3176:
-
-New KeyedTuple implementation dramatically faster
--------------------------------------------------
-
-We took a look into the :class:`.KeyedTuple` implementation in the hopes
-of improving queries like this::
-
- rows = sess.query(Foo.a, Foo.b, Foo.c).all()
-
-The :class:`.KeyedTuple` class is used rather than Python's
-``collections.namedtuple()``, because the latter has a very complex
-type-creation routine that benchmarks much slower than :class:`.KeyedTuple`.
-However, when fetching hundreds of thousands of rows,
-``collections.namedtuple()`` quickly overtakes :class:`.KeyedTuple` which
-becomes dramatically slower as instance invocation goes up. What to do?
-A new type that hedges between the approaches of both. Benching
-all three types for "size" (number of rows returned) and "num"
-(number of distinct queries), the new "lightweight keyed tuple" either
-outperforms both, or lags very slightly behind the faster object, based on
-which scenario. In the "sweet spot", where we are both creating a good number
-of new types as well as fetching a good number of rows, the lightweight
-object totally smokes both namedtuple and KeyedTuple:
-
-.. sourcecode:: text
-
- -----------------
- size=10 num=10000 # few rows, lots of queries
- namedtuple: 3.60302400589 # namedtuple falls over
- keyedtuple: 0.255059957504 # KeyedTuple very fast
- lw keyed tuple: 0.582715034485 # lw keyed trails right on KeyedTuple
- -----------------
- size=100 num=1000 # <--- sweet spot
- namedtuple: 0.365247011185
- keyedtuple: 0.24896979332
- lw keyed tuple: 0.0889317989349 # lw keyed blows both away!
- -----------------
- size=10000 num=100
- namedtuple: 0.572599887848
- keyedtuple: 2.54251694679
- lw keyed tuple: 0.613876104355
- -----------------
- size=1000000 num=10 # few queries, lots of rows
- namedtuple: 5.79669594765 # namedtuple very fast
- keyedtuple: 28.856498003 # KeyedTuple falls over
- lw keyed tuple: 6.74346804619 # lw keyed trails right on namedtuple
-
-
-:ticket:`3176`
-
-.. _feature_slots:
-
-Significant Improvements in Structural Memory Use
--------------------------------------------------
-
-Structural memory use has been improved via much more significant use
-of ``__slots__`` for many internal objects. This optimization is
-particularly geared towards the base memory size of large applications
-that have lots of tables and columns, and reduces memory
-size for a variety of high-volume objects including event listening
-internals, comparator objects and parts of the ORM attribute and
-loader strategy system.
-
-A bench that makes use of heapy measure the startup size of Nova
-illustrates a difference of about 3.7 fewer megs, or 46%,
-taken up by SQLAlchemy's objects, associated dictionaries, as
-well as weakrefs, within a basic import of "nova.db.sqlalchemy.models":
-
-.. sourcecode:: text
-
- # reported by heapy, summation of SQLAlchemy objects +
- # associated dicts + weakref-related objects with core of Nova imported:
-
- Before: total count 26477 total bytes 7975712
- After: total count 18181 total bytes 4236456
-
- # reported for the Python module space overall with the
- # core of Nova imported:
-
- Before: Partition of a set of 355558 objects. Total size = 61661760 bytes.
- After: Partition of a set of 346034 objects. Total size = 57808016 bytes.
-
-
-.. _feature_updatemany:
-
-UPDATE statements are now batched with executemany() in a flush
----------------------------------------------------------------
-
-UPDATE statements can now be batched within an ORM flush
-into more performant executemany() call, similarly to how INSERT
-statements can be batched; this will be invoked within flush
-based on the following criteria:
-
-* two or more UPDATE statements in sequence involve the identical set of
- columns to be modified.
-
-* The statement has no embedded SQL expressions in the SET clause.
-
-* The mapping does not use a :paramref:`~.orm.mapper.version_id_col`, or
- the backend dialect supports a "sane" rowcount for an executemany()
- operation; most DBAPIs support this correctly now.
-
-.. _feature_3178:
-
-
-.. _bug_3035:
-
-Session.get_bind() handles a wider variety of inheritance scenarios
--------------------------------------------------------------------
-
-The :meth:`.Session.get_bind` method is invoked whenever a query or unit
-of work flush process seeks to locate the database engine that corresponds
-to a particular class. The method has been improved to handle a variety
-of inheritance-oriented scenarios, including:
-
-* Binding to a Mixin or Abstract Class::
-
- class MyClass(SomeMixin, Base):
- __tablename__ = "my_table"
- # ...
-
-
- session = Session(binds={SomeMixin: some_engine})
-
-* Binding to inherited concrete subclasses individually based on table::
-
- class BaseClass(Base):
- __tablename__ = "base"
-
- # ...
-
-
- class ConcreteSubClass(BaseClass):
- __tablename__ = "concrete"
-
- # ...
-
- __mapper_args__ = {"concrete": True}
-
-
- session = Session(binds={base_table: some_engine, concrete_table: some_other_engine})
-
-:ticket:`3035`
-
-
-.. _bug_3227:
-
-Session.get_bind() will receive the Mapper in all relevant Query cases
-----------------------------------------------------------------------
-
-A series of issues were repaired where the :meth:`.Session.get_bind`
-would not receive the primary :class:`_orm.Mapper` of the :class:`_query.Query`,
-even though this mapper was readily available (the primary mapper is the
-single mapper, or alternatively the first mapper, that is associated with
-a :class:`_query.Query` object).
-
-The :class:`_orm.Mapper` object, when passed to :meth:`.Session.get_bind`,
-is typically used by sessions that make use of the
-:paramref:`.Session.binds` parameter to associate mappers with a
-series of engines (although in this use case, things frequently
-"worked" in most cases anyway as the bind would be located via the
-mapped table object), or more specifically implement a user-defined
-:meth:`.Session.get_bind` method that provides some pattern of
-selecting engines based on mappers, such as horizontal sharding or a
-so-called "routing" session that routes queries to different backends.
-
-These scenarios include:
-
-* :meth:`_query.Query.count`::
-
- session.query(User).count()
-
-* :meth:`_query.Query.update` and :meth:`_query.Query.delete`, both for the UPDATE/DELETE
- statement as well as for the SELECT used by the "fetch" strategy::
-
- session.query(User).filter(User.id == 15).update(
- {"name": "foob"}, synchronize_session="fetch"
- )
-
- session.query(User).filter(User.id == 15).delete(synchronize_session="fetch")
-
-* Queries against individual columns::
-
- session.query(User.id, User.name).all()
-
-* SQL functions and other expressions against indirect mappings such as
- :obj:`.column_property`::
-
- class User(Base):
- ...
-
- score = column_property(func.coalesce(self.tables.users.c.name, None))
-
-
- session.query(func.max(User.score)).scalar()
-
-:ticket:`3227` :ticket:`3242` :ticket:`1326`
-
-.. _feature_2963:
-
-.info dictionary improvements
------------------------------
-
-The :attr:`.InspectionAttr.info` collection is now available on every kind
-of object that one would retrieve from the :attr:`_orm.Mapper.all_orm_descriptors`
-collection. This includes :class:`.hybrid_property` and :func:`.association_proxy`.
-However, as these objects are class-bound descriptors, they must be accessed
-**separately** from the class to which they are attached in order to get
-at the attribute. Below this is illustrated using the
-:attr:`_orm.Mapper.all_orm_descriptors` namespace::
-
- class SomeObject(Base):
- # ...
-
- @hybrid_property
- def some_prop(self):
- return self.value + 5
-
-
- inspect(SomeObject).all_orm_descriptors.some_prop.info["foo"] = "bar"
-
-It is also available as a constructor argument for all :class:`.SchemaItem`
-objects (e.g. :class:`_schema.ForeignKey`, :class:`.UniqueConstraint` etc.) as well
-as remaining ORM constructs such as :func:`_orm.synonym`.
-
-:ticket:`2971`
-
-:ticket:`2963`
-
-.. _bug_3188:
-
-ColumnProperty constructs work a lot better with aliases, order_by
-------------------------------------------------------------------
-
-A variety of issues regarding :func:`.column_property` have been fixed,
-most specifically with regards to the :func:`.aliased` construct as well
-as the "order by label" logic introduced in 0.9 (see :ref:`migration_1068`).
-
-Given a mapping like the following::
-
- class A(Base):
- __tablename__ = "a"
-
- id = Column(Integer, primary_key=True)
-
-
- class B(Base):
- __tablename__ = "b"
-
- id = Column(Integer, primary_key=True)
- a_id = Column(ForeignKey("a.id"))
-
-
- A.b = column_property(select([func.max(B.id)]).where(B.a_id == A.id).correlate(A))
-
-A simple scenario that included "A.b" twice would fail to render
-correctly::
-
- print(sess.query(A, a1).order_by(a1.b))
-
-This would order by the wrong column:
-
-.. sourcecode:: sql
-
- SELECT a.id AS a_id, (SELECT max(b.id) AS max_1 FROM b
- WHERE b.a_id = a.id) AS anon_1, a_1.id AS a_1_id,
- (SELECT max(b.id) AS max_2
- FROM b WHERE b.a_id = a_1.id) AS anon_2
- FROM a, a AS a_1 ORDER BY anon_1
-
-New output:
-
-.. sourcecode:: sql
-
- SELECT a.id AS a_id, (SELECT max(b.id) AS max_1
- FROM b WHERE b.a_id = a.id) AS anon_1, a_1.id AS a_1_id,
- (SELECT max(b.id) AS max_2
- FROM b WHERE b.a_id = a_1.id) AS anon_2
- FROM a, a AS a_1 ORDER BY anon_2
-
-There were also many scenarios where the "order by" logic would fail
-to order by label, for example if the mapping were "polymorphic"::
-
- class A(Base):
- __tablename__ = "a"
-
- id = Column(Integer, primary_key=True)
- type = Column(String)
-
- __mapper_args__ = {"polymorphic_on": type, "with_polymorphic": "*"}
-
-The order_by would fail to use the label, as it would be anonymized due
-to the polymorphic loading:
-
-.. sourcecode:: sql
-
- SELECT a.id AS a_id, a.type AS a_type, (SELECT max(b.id) AS max_1
- FROM b WHERE b.a_id = a.id) AS anon_1
- FROM a ORDER BY (SELECT max(b.id) AS max_2
- FROM b WHERE b.a_id = a.id)
-
-Now that the order by label tracks the anonymized label, this now works:
-
-.. sourcecode:: sql
-
- SELECT a.id AS a_id, a.type AS a_type, (SELECT max(b.id) AS max_1
- FROM b WHERE b.a_id = a.id) AS anon_1
- FROM a ORDER BY anon_1
-
-Included in these fixes are a variety of heisenbugs that could corrupt
-the state of an ``aliased()`` construct such that the labeling logic
-would again fail; these have also been fixed.
-
-:ticket:`3148` :ticket:`3188`
-
-New Features and Improvements - Core
-====================================
-
-.. _feature_3034:
-
-Select/Query LIMIT / OFFSET may be specified as an arbitrary SQL expression
----------------------------------------------------------------------------
-
-The :meth:`_expression.Select.limit` and :meth:`_expression.Select.offset` methods now accept
-any SQL expression, in addition to integer values, as arguments. The ORM
-:class:`_query.Query` object also passes through any expression to the underlying
-:class:`_expression.Select` object. Typically
-this is used to allow a bound parameter to be passed, which can be substituted
-with a value later::
-
- sel = select([table]).limit(bindparam("mylimit")).offset(bindparam("myoffset"))
-
-Dialects which don't support non-integer LIMIT or OFFSET expressions may continue
-to not support this behavior; third party dialects may also need modification
-in order to take advantage of the new behavior. A dialect which currently
-uses the ``._limit`` or ``._offset`` attributes will continue to function
-for those cases where the limit/offset was specified as a simple integer value.
-However, when a SQL expression is specified, these two attributes will
-instead raise a :class:`.CompileError` on access. A third-party dialect which
-wishes to support the new feature should now call upon the ``._limit_clause``
-and ``._offset_clause`` attributes to receive the full SQL expression, rather
-than the integer value.
-
-.. _feature_3282:
-
-The ``use_alter`` flag on ``ForeignKeyConstraint`` is (usually) no longer needed
---------------------------------------------------------------------------------
-
-The :meth:`_schema.MetaData.create_all` and :meth:`_schema.MetaData.drop_all` methods will
-now make use of a system that automatically renders an ALTER statement
-for foreign key constraints that are involved in mutually-dependent cycles
-between tables, without the
-need to specify :paramref:`_schema.ForeignKeyConstraint.use_alter`. Additionally,
-the foreign key constraints no longer need to have a name in order to be
-created via ALTER; only the DROP operation requires a name. In the case
-of a DROP, the feature will ensure that only constraints which have
-explicit names are actually included as ALTER statements. In the
-case of an unresolvable cycle within a DROP, the system emits
-a succinct and clear error message now if the DROP cannot proceed.
-
-The :paramref:`_schema.ForeignKeyConstraint.use_alter` and
-:paramref:`_schema.ForeignKey.use_alter` flags remain in place, and continue to have
-the same effect of establishing those constraints for which ALTER is
-required during a CREATE/DROP scenario.
-
-As of version 1.0.1, special logic takes over in the case of SQLite, which
-does not support ALTER, in the case that during a DROP, the given tables have
-an unresolvable cycle; in this case a warning is emitted, and the tables
-are dropped with **no** ordering, which is usually fine on SQLite unless
-constraints are enabled. To resolve the warning and proceed with at least
-a partial ordering on a SQLite database, particularly one where constraints
-are enabled, re-apply "use_alter" flags to those
-:class:`_schema.ForeignKey` and :class:`_schema.ForeignKeyConstraint` objects which should
-be explicitly omitted from the sort.
-
-.. seealso::
-
- :ref:`use_alter` - full description of the new behavior.
-
-:ticket:`3282`
-
-.. _change_3330:
-
-ResultProxy "auto close" is now a "soft" close
-----------------------------------------------
-
-For many releases, the :class:`_engine.ResultProxy` object has always been
-automatically closed out at the point at which all result rows have been
-fetched. This was to allow usage of the object without the need to call
-upon :meth:`_engine.ResultProxy.close` explicitly; as all DBAPI resources had been
-freed, the object was safe to discard. However, the object maintained
-a strict "closed" behavior, which meant that any subsequent calls to
-:meth:`_engine.ResultProxy.fetchone`, :meth:`_engine.ResultProxy.fetchmany` or
-:meth:`_engine.ResultProxy.fetchall` would now raise a :class:`.ResourceClosedError`::
-
- >>> result = connection.execute(stmt)
- >>> result.fetchone()
- (1, 'x')
- >>> result.fetchone()
- None # indicates no more rows
- >>> result.fetchone()
- exception: ResourceClosedError
-
-This behavior is inconsistent vs. what pep-249 states, which is
-that you can call upon the fetch methods repeatedly even after results
-are exhausted. It also interferes with behavior for some implementations of
-result proxy, such as the :class:`.BufferedColumnResultProxy` used by the
-cx_oracle dialect for certain datatypes.
-
-To solve this, the "closed" state of the :class:`_engine.ResultProxy` has been
-broken into two states; a "soft close" which does the majority of what
-"close" does, in that it releases the DBAPI cursor and in the case of a
-"close with result" object will also release the connection, and a
-"closed" state which is everything included by "soft close" as well as
-establishing the fetch methods as "closed". The :meth:`_engine.ResultProxy.close`
-method is now never called implicitly, only the :meth:`_engine.ResultProxy._soft_close`
-method which is non-public::
-
- >>> result = connection.execute(stmt)
- >>> result.fetchone()
- (1, 'x')
- >>> result.fetchone()
- None # indicates no more rows
- >>> result.fetchone()
- None # still None
- >>> result.fetchall()
- []
- >>> result.close()
- >>> result.fetchone()
- exception: ResourceClosedError # *now* it raises
-
-:ticket:`3330`
-:ticket:`3329`
-
-CHECK Constraints now support the ``%(column_0_name)s`` token in naming conventions
------------------------------------------------------------------------------------
-
-The ``%(column_0_name)s`` will derive from the first column found in the
-expression of a :class:`.CheckConstraint`::
-
- metadata = MetaData(naming_convention={"ck": "ck_%(table_name)s_%(column_0_name)s"})
-
- foo = Table("foo", metadata, Column("value", Integer))
-
- CheckConstraint(foo.c.value > 5)
-
-Will render:
-
-.. sourcecode:: sql
-
- CREATE TABLE foo (
- value INTEGER,
- CONSTRAINT ck_foo_value CHECK (value > 5)
- )
-
-The combination of naming conventions with the constraint produced by a
-:class:`.SchemaType` such as :class:`.Boolean` or :class:`.Enum` will also
-now make use of all CHECK constraint conventions.
-
-.. seealso::
-
- :ref:`naming_check_constraints`
-
- :ref:`naming_schematypes`
-
-:ticket:`3299`
-
-.. _change_3341:
-
-Constraints referring to unattached Columns can auto-attach to the Table when their referred columns are attached
------------------------------------------------------------------------------------------------------------------
-
-Since at least version 0.8, a :class:`.Constraint` has had the ability to
-"auto-attach" itself to a :class:`_schema.Table` based on being passed table-attached columns::
-
- from sqlalchemy import Table, Column, MetaData, Integer, UniqueConstraint
-
- m = MetaData()
-
- t = Table("t", m, Column("a", Integer), Column("b", Integer))
-
- uq = UniqueConstraint(t.c.a, t.c.b) # will auto-attach to Table
-
- assert uq in t.constraints
-
-In order to assist with some cases that tend to come up with declarative,
-this same auto-attachment logic can now function even if the :class:`_schema.Column`
-objects are not yet associated with the :class:`_schema.Table`; additional events
-are established such that when those :class:`_schema.Column` objects are associated,
-the :class:`.Constraint` is also added::
-
- from sqlalchemy import Table, Column, MetaData, Integer, UniqueConstraint
-
- m = MetaData()
-
- a = Column("a", Integer)
- b = Column("b", Integer)
-
- uq = UniqueConstraint(a, b)
-
- t = Table("t", m, a, b)
-
- assert uq in t.constraints # constraint auto-attached
-
-The above feature was a late add as of version 1.0.0b3. A fix as of
-version 1.0.4 for :ticket:`3411` ensures that this logic
-does not occur if the :class:`.Constraint` refers to a mixture of
-:class:`_schema.Column` objects and string column names; as we do not yet have
-tracking for the addition of names to a :class:`_schema.Table`::
-
- from sqlalchemy import Table, Column, MetaData, Integer, UniqueConstraint
-
- m = MetaData()
-
- a = Column("a", Integer)
- b = Column("b", Integer)
-
- uq = UniqueConstraint(a, "b")
-
- t = Table("t", m, a, b)
-
- # constraint *not* auto-attached, as we do not have tracking
- # to locate when a name 'b' becomes available on the table
- assert uq not in t.constraints
-
-Above, the attachment event for column "a" to table "t" will fire off before
-column "b" is attached (as "a" is stated in the :class:`_schema.Table` constructor
-before "b"), and the constraint will fail to locate "b" if it were to attempt
-an attachment. For consistency, if the constraint refers to any string names,
-the autoattach-on-column-attach logic is skipped.
-
-The original auto-attach logic of course remains in place, if the :class:`_schema.Table`
-already contains all the target :class:`_schema.Column` objects at the time
-the :class:`.Constraint` is constructed::
-
- from sqlalchemy import Table, Column, MetaData, Integer, UniqueConstraint
-
- m = MetaData()
-
- a = Column("a", Integer)
- b = Column("b", Integer)
-
-
- t = Table("t", m, a, b)
-
- uq = UniqueConstraint(a, "b")
-
- # constraint auto-attached normally as in older versions
- assert uq in t.constraints
-
-:ticket:`3341`
-:ticket:`3411`
-
-.. _change_2051:
-
-.. _feature_insert_from_select_defaults:
-
-INSERT FROM SELECT now includes Python and SQL-expression defaults
-------------------------------------------------------------------
-
-:meth:`_expression.Insert.from_select` now includes Python and SQL-expression defaults if
-otherwise unspecified; the limitation where non-server column defaults
-aren't included in an INSERT FROM SELECT is now lifted and these
-expressions are rendered as constants into the SELECT statement::
-
- from sqlalchemy import Table, Column, MetaData, Integer, select, func
-
- m = MetaData()
-
- t = Table(
- "t", m, Column("x", Integer), Column("y", Integer, default=func.somefunction())
- )
-
- stmt = select([t.c.x])
- print(t.insert().from_select(["x"], stmt))
-
-Will render:
-
-.. sourcecode:: sql
-
- INSERT INTO t (x, y) SELECT t.x, somefunction() AS somefunction_1
- FROM t
-
-The feature can be disabled using
-:paramref:`.Insert.from_select.include_defaults`.
-
-.. _change_3087:
-
-Column server defaults now render literal values
-------------------------------------------------
-
-The "literal binds" compiler flag is switched on when a
-:class:`.DefaultClause`, set up by :paramref:`_schema.Column.server_default`
-is present as a SQL expression to be compiled. This allows literals
-embedded in SQL to render correctly, such as::
-
- from sqlalchemy import Table, Column, MetaData, Text
- from sqlalchemy.schema import CreateTable
- from sqlalchemy.dialects.postgresql import ARRAY, array
- from sqlalchemy.dialects import postgresql
-
- metadata = MetaData()
-
- tbl = Table(
- "derp",
- metadata,
- Column("arr", ARRAY(Text), server_default=array(["foo", "bar", "baz"])),
- )
-
- print(CreateTable(tbl).compile(dialect=postgresql.dialect()))
-
-Now renders:
-
-.. sourcecode:: sql
-
- CREATE TABLE derp (
- arr TEXT[] DEFAULT ARRAY['foo', 'bar', 'baz']
- )
-
-Previously, the literal values ``"foo", "bar", "baz"`` would render as
-bound parameters, which are useless in DDL.
-
-:ticket:`3087`
-
-.. _feature_3184:
-
-UniqueConstraint is now part of the Table reflection process
-------------------------------------------------------------
-
-A :class:`_schema.Table` object populated using ``autoload=True`` will now
-include :class:`.UniqueConstraint` constructs as well as
-:class:`.Index` constructs. This logic has a few caveats for
-PostgreSQL and MySQL:
-
-PostgreSQL
-^^^^^^^^^^
-
-PostgreSQL has the behavior such that when a UNIQUE constraint is
-created, it implicitly creates a UNIQUE INDEX corresponding to that
-constraint as well. The :meth:`_reflection.Inspector.get_indexes` and the
-:meth:`_reflection.Inspector.get_unique_constraints` methods will continue to
-**both** return these entries distinctly, where
-:meth:`_reflection.Inspector.get_indexes` now features a token
-``duplicates_constraint`` within the index entry indicating the
-corresponding constraint when detected. However, when performing
-full table reflection using ``Table(..., autoload=True)``, the
-:class:`.Index` construct is detected as being linked to the
-:class:`.UniqueConstraint`, and is **not** present within the
-:attr:`_schema.Table.indexes` collection; only the :class:`.UniqueConstraint`
-will be present in the :attr:`_schema.Table.constraints` collection. This
-deduplication logic works by joining to the ``pg_constraint`` table
-when querying ``pg_index`` to see if the two constructs are linked.
-
-MySQL
-^^^^^
-
-MySQL does not have separate concepts for a UNIQUE INDEX and a UNIQUE
-constraint. While it supports both syntaxes when creating tables and indexes,
-it does not store them any differently. The
-:meth:`_reflection.Inspector.get_indexes`
-and the :meth:`_reflection.Inspector.get_unique_constraints` methods will continue to
-**both** return an entry for a UNIQUE index in MySQL,
-where :meth:`_reflection.Inspector.get_unique_constraints` features a new token
-``duplicates_index`` within the constraint entry indicating that this is a
-dupe entry corresponding to that index. However, when performing
-full table reflection using ``Table(..., autoload=True)``,
-the :class:`.UniqueConstraint` construct is
-**not** part of the fully reflected :class:`_schema.Table` construct under any
-circumstances; this construct is always represented by a :class:`.Index`
-with the ``unique=True`` setting present in the :attr:`_schema.Table.indexes`
-collection.
-
-.. seealso::
-
- :ref:`postgresql_index_reflection`
-
- :ref:`mysql_unique_constraints`
-
-:ticket:`3184`
-
-
-New systems to safely emit parameterized warnings
--------------------------------------------------
-
-For a long time, there has been a restriction that warning messages could not
-refer to data elements, such that a particular function might emit an
-infinite number of unique warnings. The key place this occurs is in the
-``Unicode type received non-unicode bind param value`` warning. Placing
-the data value in this message would mean that the Python ``__warningregistry__``
-for that module, or in some cases the Python-global ``warnings.onceregistry``,
-would grow unbounded, as in most warning scenarios, one of these two collections
-is populated with every distinct warning message.
-
-The change here is that by using a special ``string`` type that purposely
-changes how the string is hashed, we can control that a large number of
-parameterized messages are hashed only on a small set of possible hash
-values, such that a warning such as ``Unicode type received non-unicode
-bind param value`` can be tailored to be emitted only a specific number
-of times; beyond that, the Python warnings registry will begin recording
-them as duplicates.
-
-To illustrate, the following test script will show only ten warnings being
-emitted for ten of the parameter sets, out of a total of 1000::
-
- from sqlalchemy import create_engine, Unicode, select, cast
- import random
- import warnings
-
- e = create_engine("sqlite://")
-
- # Use the "once" filter (which is also the default for Python
- # warnings). Exactly ten of these warnings will
- # be emitted; beyond that, the Python warnings registry will accumulate
- # new values as dupes of one of the ten existing.
- warnings.filterwarnings("once")
-
- for i in range(1000):
- e.execute(
- select([cast(("foo_%d" % random.randint(0, 1000000)).encode("ascii"), Unicode)])
- )
-
-The format of the warning here is:
-
-.. sourcecode:: text
-
- /path/lib/sqlalchemy/sql/sqltypes.py:186: SAWarning: Unicode type received
- non-unicode bind param value 'foo_4852'. (this warning may be
- suppressed after 10 occurrences)
-
-
-:ticket:`3178`
-
-Key Behavioral Changes - ORM
-============================
-
-.. _bug_3228:
-
-query.update() now resolves string names into mapped attribute names
---------------------------------------------------------------------
-
-The documentation for :meth:`_query.Query.update` states that the given
-``values`` dictionary is "a dictionary with attributes names as keys",
-implying that these are mapped attribute names. Unfortunately, the function
-was designed more in mind to receive attributes and SQL expressions and
-not as much strings; when strings
-were passed, these strings would be passed through straight to the core
-update statement without any resolution as far as how these names are
-represented on the mapped class, meaning the name would have to match that
-of a table column exactly, not how an attribute of that name was mapped
-onto the class.
-
-The string names are now resolved as attribute names in earnest::
-
- class User(Base):
- __tablename__ = "user"
-
- id = Column(Integer, primary_key=True)
- name = Column("user_name", String(50))
-
-Above, the column ``user_name`` is mapped as ``name``. Previously,
-a call to :meth:`_query.Query.update` that was passed strings would have to
-have been called as follows::
-
- session.query(User).update({"user_name": "moonbeam"})
-
-The given string is now resolved against the entity::
-
- session.query(User).update({"name": "moonbeam"})
-
-It is typically preferable to use the attribute directly, to avoid any
-ambiguity::
-
- session.query(User).update({User.name: "moonbeam"})
-
-The change also indicates that synonyms and hybrid attributes can be referred
-to by string name as well::
-
- class User(Base):
- __tablename__ = "user"
-
- id = Column(Integer, primary_key=True)
- name = Column("user_name", String(50))
-
- @hybrid_property
- def fullname(self):
- return self.name
-
-
- session.query(User).update({"fullname": "moonbeam"})
-
-:ticket:`3228`
-
-.. _bug_3371:
-
-Warnings emitted when comparing objects with None values to relationships
--------------------------------------------------------------------------
-
-This change is new as of 1.0.1. Some users are performing
-queries that are essentially of this form::
-
- session.query(Address).filter(Address.user == User(id=None))
-
-This pattern is not currently supported in SQLAlchemy. For all versions,
-it emits SQL resembling:
-
-.. sourcecode:: sql
-
- SELECT address.id AS address_id, address.user_id AS address_user_id,
- address.email_address AS address_email_address
- FROM address WHERE ? = address.user_id
- (None,)
-
-Note above, there is a comparison ``WHERE ? = address.user_id`` where the
-bound value ``?`` is receiving ``None``, or ``NULL`` in SQL. **This will
-always return False in SQL**. The comparison here would in theory
-generate SQL as follows:
-
-.. sourcecode:: sql
-
- SELECT address.id AS address_id, address.user_id AS address_user_id,
- address.email_address AS address_email_address
- FROM address WHERE address.user_id IS NULL
-
-But right now, **it does not**. Applications which are relying upon the
-fact that "NULL = NULL" produces False in all cases run the risk that
-someday, SQLAlchemy might fix this issue to generate "IS NULL", and the queries
-will then produce different results. Therefore with this kind of operation,
-you will see a warning:
-
-.. sourcecode:: text
-
- SAWarning: Got None for value of column user.id; this is unsupported
- for a relationship comparison and will not currently produce an
- IS comparison (but may in a future release)
-
-Note that this pattern was broken in most cases for release 1.0.0 including
-all of the betas; a value like ``SYMBOL('NEVER_SET')`` would be generated.
-This issue has been fixed, but as a result of identifying this pattern,
-the warning is now there so that we can more safely repair this broken
-behavior (now captured in :ticket:`3373`) in a future release.
-
-:ticket:`3371`
-
-.. _bug_3374:
-
-A "negated contains or equals" relationship comparison will use the current value of attributes, not the database value
--------------------------------------------------------------------------------------------------------------------------
-
-This change is new as of 1.0.1; while we would have preferred for this to be in 1.0.0,
-it only became apparent as a result of :ticket:`3371`.
-
-Given a mapping::
-
- class A(Base):
- __tablename__ = "a"
- id = Column(Integer, primary_key=True)
-
-
- class B(Base):
- __tablename__ = "b"
- id = Column(Integer, primary_key=True)
- a_id = Column(ForeignKey("a.id"))
- a = relationship("A")
-
-Given ``A``, with primary key of 7, but which we changed to be 10
-without flushing::
-
- s = Session(autoflush=False)
- a1 = A(id=7)
- s.add(a1)
- s.commit()
-
- a1.id = 10
-
-A query against a many-to-one relationship with this object as the target
-will use the value 10 in the bound parameters::
-
- s.query(B).filter(B.a == a1)
-
-Produces:
-
-.. sourcecode:: sql
-
- SELECT b.id AS b_id, b.a_id AS b_a_id
- FROM b
- WHERE ? = b.a_id
- (10,)
-
-However, before this change, the negation of this criteria would **not** use
-10, it would use 7, unless the object were flushed first::
-
- s.query(B).filter(B.a != a1)
-
-Produces (in 0.9 and all versions prior to 1.0.1):
-
-.. sourcecode:: sql
-
- SELECT b.id AS b_id, b.a_id AS b_a_id
- FROM b
- WHERE b.a_id != ? OR b.a_id IS NULL
- (7,)
-
-For a transient object, it would produce a broken query:
-
-.. sourcecode:: sql
-
- SELECT b.id, b.a_id
- FROM b
- WHERE b.a_id != :a_id_1 OR b.a_id IS NULL
- -- {u'a_id_1': symbol('NEVER_SET')}
-
-This inconsistency has been repaired, and in all queries the current attribute
-value, in this example ``10``, will now be used.
-
-:ticket:`3374`
-
-.. _migration_3061:
-
-Changes to attribute events and other operations regarding attributes that have no pre-existing value
-------------------------------------------------------------------------------------------------------
-
-In this change, the default return value of ``None`` when accessing an object
-is now returned dynamically on each access, rather than implicitly setting the
-attribute's state with a special "set" operation when it is first accessed.
-The visible result of this change is that ``obj.__dict__`` is not implicitly
-modified on get, and there are also some minor behavioral changes
-for :func:`.attributes.get_history` and related functions.
-
-Given an object with no state::
-
- >>> obj = Foo()
-
-It has always been SQLAlchemy's behavior such that if we access a scalar
-or many-to-one attribute that was never set, it is returned as ``None``::
-
- >>> obj.someattr
- None
-
-This value of ``None`` is in fact now part of the state of ``obj``, and is
-not unlike as though we had set the attribute explicitly, e.g.
-``obj.someattr = None``. However, the "set on get" here would behave
-differently as far as history and events. It would not emit any attribute
-event, and additionally if we view history, we see this::
-
- >>> inspect(obj).attrs.someattr.history
- History(added=(), unchanged=[None], deleted=()) # 0.9 and below
-
-That is, it's as though the attribute were always ``None`` and were
-never changed. This is explicitly different from if we had set the
-attribute first instead::
-
- >>> obj = Foo()
- >>> obj.someattr = None
- >>> inspect(obj).attrs.someattr.history
- History(added=[None], unchanged=(), deleted=()) # all versions
-
-The above means that the behavior of our "set" operation can be corrupted
-by the fact that the value was accessed via "get" earlier. In 1.0, this
-inconsistency has been resolved, by no longer actually setting anything
-when the default "getter" is used.
-
- >>> obj = Foo()
- >>> obj.someattr
- None
- >>> inspect(obj).attrs.someattr.history
- History(added=(), unchanged=(), deleted=()) # 1.0
- >>> obj.someattr = None
- >>> inspect(obj).attrs.someattr.history
- History(added=[None], unchanged=(), deleted=())
-
-The reason the above behavior hasn't had much impact is because the
-INSERT statement in relational databases considers a missing value to be
-the same as NULL in most cases. Whether SQLAlchemy received a history
-event for a particular attribute set to None or not would usually not matter;
-as the difference between sending None/NULL or not wouldn't have an impact.
-However, as :ticket:`3060` (described here in :ref:`migration_3060`)
-illustrates, there are some seldom edge cases
-where we do in fact want to positively have ``None`` set. Also, allowing
-the attribute event here means it's now possible to create "default value"
-functions for ORM mapped attributes.
-
-As part of this change, the generation of the implicit "None" is now disabled
-for other situations where this used to occur; this includes when an
-attribute set operation on a many-to-one is received; previously, the "old" value
-would be "None" if it had been not set otherwise; it now will send the
-value :data:`.orm.attributes.NEVER_SET`, which is a value that may be sent
-to an attribute listener now. This symbol may also be received when
-calling on mapper utility functions such as :meth:`_orm.Mapper.primary_key_from_instance`;
-if the primary key attributes have no setting at all, whereas the value
-would be ``None`` before, it will now be the :data:`.orm.attributes.NEVER_SET`
-symbol, and no change to the object's state occurs.
-
-:ticket:`3061`
-
-.. _migration_3060:
-
-Priority of attribute changes on relationship-bound attributes vs. FK-bound may appear to change
-------------------------------------------------------------------------------------------------
-
-As a side effect of :ticket:`3060`, setting a relationship-bound attribute to ``None``
-is now a tracked history event which refers to the intention of persisting
-``None`` to that attribute. As it has always been the case that setting a
-relationship-bound attribute will trump direct assignment to the foreign key
-attributes, a change in behavior can be seen here when assigning None.
-Given a mapping::
-
- class A(Base):
- __tablename__ = "table_a"
-
- id = Column(Integer, primary_key=True)
-
-
- class B(Base):
- __tablename__ = "table_b"
-
- id = Column(Integer, primary_key=True)
- a_id = Column(ForeignKey("table_a.id"))
- a = relationship(A)
-
-In 1.0, the relationship-bound attribute takes precedence over the FK-bound
-attribute in all cases, whether or not
-the value we assign is a reference to an ``A`` object or is ``None``.
-In 0.9, the behavior is inconsistent and
-only takes effect if a value is assigned; the None is not considered::
-
- a1 = A(id=1)
- a2 = A(id=2)
- session.add_all([a1, a2])
- session.flush()
-
- b1 = B()
- b1.a = a1 # we expect a_id to be '1'; takes precedence in 0.9 and 1.0
-
- b2 = B()
- b2.a = None # we expect a_id to be None; takes precedence only in 1.0
-
- b1.a_id = 2
- b2.a_id = 2
-
- session.add_all([b1, b2])
- session.commit()
-
- assert b1.a is a1 # passes in both 0.9 and 1.0
- assert b2.a is None # passes in 1.0, in 0.9 it's a2
-
-:ticket:`3060`
-
-.. _bug_3139:
-
-session.expunge() will fully detach an object that's been deleted
------------------------------------------------------------------
-
-The behavior of :meth:`.Session.expunge` had a bug that caused an
-inconsistency in behavior regarding deleted objects. The
-:func:`.object_session` function as well as the :attr:`.InstanceState.session`
-attribute would still report object as belonging to the :class:`.Session`
-subsequent to the expunge::
-
- u1 = sess.query(User).first()
- sess.delete(u1)
-
- sess.flush()
-
- assert u1 not in sess
- assert inspect(u1).session is sess # this is normal before commit
-
- sess.expunge(u1)
-
- assert u1 not in sess
- assert inspect(u1).session is None # would fail
-
-Note that it is normal for ``u1 not in sess`` to be True while
-``inspect(u1).session`` still refers to the session, while the transaction
-is ongoing subsequent to the delete operation and :meth:`.Session.expunge`
-has not been called; the full detachment normally completes once the
-transaction is committed. This issue would also impact functions
-that rely on :meth:`.Session.expunge` such as :func:`.make_transient`.
-
-:ticket:`3139`
-
-.. _migration_yield_per_eager_loading:
-
-Joined/Subquery eager loading explicitly disallowed with yield_per
-------------------------------------------------------------------
-
-In order to make the :meth:`_query.Query.yield_per` method easier to use,
-an exception is raised if any subquery eager loaders, or joined
-eager loaders that would use collections, are
-to take effect when yield_per is used, as these are currently not compatible
-with yield-per (subquery loading could be in theory, however).
-When this error is raised, the :func:`.lazyload` option can be sent with
-an asterisk::
-
- q = sess.query(Object).options(lazyload("*")).yield_per(100)
-
-or use :meth:`_query.Query.enable_eagerloads`::
-
- q = sess.query(Object).enable_eagerloads(False).yield_per(100)
-
-The :func:`.lazyload` option has the advantage that additional many-to-one
-joined loader options can still be used::
-
- q = (
- sess.query(Object)
- .options(lazyload("*"), joinedload("some_manytoone"))
- .yield_per(100)
- )
-
-.. _bug_3233:
-
-Changes and fixes in handling of duplicate join targets
--------------------------------------------------------
-
-Changes here encompass bugs where an unexpected and inconsistent
-behavior would occur in some scenarios when joining to an entity
-twice, or to multiple single-table entities against the same table,
-without using a relationship-based ON clause, as well as when joining
-multiple times to the same target relationship.
-
-Starting with a mapping as::
-
- from sqlalchemy import Integer, Column, String, ForeignKey
- from sqlalchemy.orm import Session, relationship
- from sqlalchemy.ext.declarative import declarative_base
-
- Base = declarative_base()
-
-
- class A(Base):
- __tablename__ = "a"
- id = Column(Integer, primary_key=True)
- bs = relationship("B")
-
-
- class B(Base):
- __tablename__ = "b"
- id = Column(Integer, primary_key=True)
- a_id = Column(ForeignKey("a.id"))
-
-A query that joins to ``A.bs`` twice::
-
- print(s.query(A).join(A.bs).join(A.bs))
-
-Will render:
-
-.. sourcecode:: sql
-
- SELECT a.id AS a_id
- FROM a JOIN b ON a.id = b.a_id
-
-The query deduplicates the redundant ``A.bs`` because it is attempting
-to support a case like the following::
-
- s.query(A).join(A.bs).filter(B.foo == "bar").reset_joinpoint().join(A.bs, B.cs).filter(
- C.bar == "bat"
- )
-
-That is, the ``A.bs`` is part of a "path". As part of :ticket:`3367`,
-arriving at the same endpoint twice without it being part of a
-larger path will now emit a warning:
-
-.. sourcecode:: text
-
- SAWarning: Pathed join target A.bs has already been joined to; skipping
-
-The bigger change involves when joining to an entity without using a
-relationship-bound path. If we join to ``B`` twice::
-
- print(s.query(A).join(B, B.a_id == A.id).join(B, B.a_id == A.id))
-
-In 0.9, this would render as follows:
-
-.. sourcecode:: sql
-
- SELECT a.id AS a_id
- FROM a JOIN b ON b.a_id = a.id JOIN b AS b_1 ON b_1.a_id = a.id
-
-This is problematic since the aliasing is implicit and in the case of different
-ON clauses can lead to unpredictable results.
-
-In 1.0, no automatic aliasing is applied and we get:
-
-.. sourcecode:: sql
-
- SELECT a.id AS a_id
- FROM a JOIN b ON b.a_id = a.id JOIN b ON b.a_id = a.id
-
-This will raise an error from the database. While it might be nice if
-the "duplicate join target" acted identically if we joined both from
-redundant relationships vs. redundant non-relationship based targets,
-for now we are only changing the behavior in the more serious case where
-implicit aliasing would have occurred previously, and only emitting a warning
-in the relationship case. Ultimately, joining to the same thing twice without
-any aliasing to disambiguate should raise an error in all cases.
-
-The change also has an impact on single-table inheritance targets. Using
-a mapping as follows::
-
- from sqlalchemy import Integer, Column, String, ForeignKey
- from sqlalchemy.orm import Session, relationship
- from sqlalchemy.ext.declarative import declarative_base
-
- Base = declarative_base()
-
-
- class A(Base):
- __tablename__ = "a"
-
- id = Column(Integer, primary_key=True)
- type = Column(String)
-
- __mapper_args__ = {"polymorphic_on": type, "polymorphic_identity": "a"}
-
-
- class ASub1(A):
- __mapper_args__ = {"polymorphic_identity": "asub1"}
-
-
- class ASub2(A):
- __mapper_args__ = {"polymorphic_identity": "asub2"}
-
-
- class B(Base):
- __tablename__ = "b"
-
- id = Column(Integer, primary_key=True)
-
- a_id = Column(Integer, ForeignKey("a.id"))
-
- a = relationship("A", primaryjoin="B.a_id == A.id", backref="b")
-
-
- s = Session()
-
- print(s.query(ASub1).join(B, ASub1.b).join(ASub2, B.a))
-
- print(s.query(ASub1).join(B, ASub1.b).join(ASub2, ASub2.id == B.a_id))
-
-The two queries at the bottom are equivalent, and should both render
-the identical SQL:
-
-.. sourcecode:: sql
-
- SELECT a.id AS a_id, a.type AS a_type
- FROM a JOIN b ON b.a_id = a.id JOIN a ON b.a_id = a.id AND a.type IN (:type_1)
- WHERE a.type IN (:type_2)
-
-The above SQL is invalid, as it renders "a" within the FROM list twice.
-However, the implicit aliasing bug would occur with the second query only
-and render this instead:
-
-.. sourcecode:: sql
-
- SELECT a.id AS a_id, a.type AS a_type
- FROM a JOIN b ON b.a_id = a.id JOIN a AS a_1
- ON a_1.id = b.a_id AND a_1.type IN (:type_1)
- WHERE a_1.type IN (:type_2)
-
-Where above, the second join to "a" is aliased. While this seems convenient,
-it's not how single-inheritance queries work in general and is misleading
-and inconsistent.
-
-The net effect is that applications which were relying on this bug will now
-have an error raised by the database. The solution is to use the expected
-form. When referring to multiple subclasses of a single-inheritance
-entity in a query, you must manually use aliases to disambiguate the table,
-as all the subclasses normally refer to the same table::
-
- asub2_alias = aliased(ASub2)
-
- print(s.query(ASub1).join(B, ASub1.b).join(asub2_alias, B.a.of_type(asub2_alias)))
-
-:ticket:`3233`
-:ticket:`3367`
-
-
-Deferred Columns No Longer Implicitly Undefer
----------------------------------------------
-
-Mapped attributes marked as deferred without explicit undeferral
-will now remain "deferred" even if their column is otherwise
-present in the result set in some way. This is a performance
-enhancement in that an ORM load no longer spends time searching
-for each deferred column when the result set is obtained. However,
-for an application that has been relying upon this, an explicit
-:func:`.undefer` or similar option should now be used, in order
-to prevent a SELECT from being emitted when the attribute is accessed.
-
-
-.. _migration_deprecated_orm_events:
-
-Deprecated ORM Event Hooks Removed
-----------------------------------
-
-The following ORM event hooks, some of which have been deprecated since
-0.5, have been removed: ``translate_row``, ``populate_instance``,
-``append_result``, ``create_instance``. The use cases for these hooks
-originated in the very early 0.1 / 0.2 series of SQLAlchemy and have long
-since been unnecessary. In particular, the hooks were largely unusable
-as the behavioral contracts within these events was strongly linked to
-the surrounding internals, such as how an instance needs to be created
-and initialized as well as how columns are located within an ORM-generated
-row. The removal of these hooks greatly simplifies the mechanics of ORM
-object loading.
-
-.. _bundle_api_change:
-
-API Change for new Bundle feature when custom row loaders are used
-------------------------------------------------------------------
-
-The new :class:`.Bundle` object of 0.9 has a small change in API,
-when the ``create_row_processor()`` method is overridden on a custom class.
-Previously, the sample code looked like::
-
- from sqlalchemy.orm import Bundle
-
-
- class DictBundle(Bundle):
- def create_row_processor(self, query, procs, labels):
- """Override create_row_processor to return values as dictionaries"""
-
- def proc(row, result):
- return dict(zip(labels, (proc(row, result) for proc in procs)))
-
- return proc
-
-The unused ``result`` member is now removed::
-
- from sqlalchemy.orm import Bundle
-
-
- class DictBundle(Bundle):
- def create_row_processor(self, query, procs, labels):
- """Override create_row_processor to return values as dictionaries"""
-
- def proc(row):
- return dict(zip(labels, (proc(row) for proc in procs)))
-
- return proc
-
-.. seealso::
-
- :ref:`bundles`
-
-.. _migration_3008:
-
-Right inner join nesting now the default for joinedload with innerjoin=True
----------------------------------------------------------------------------
-
-The behavior of :paramref:`_orm.joinedload.innerjoin` as well as
-:paramref:`_orm.relationship.innerjoin` is now to use "nested"
-inner joins, that is, right-nested, as the default behavior when an
-inner join joined eager load is chained to an outer join eager load. In
-order to get the old behavior of chaining all joined eager loads as
-outer join when an outer join is present, use ``innerjoin="unnested"``.
-
-As introduced in :ref:`feature_2976` from version 0.9, the behavior of
-``innerjoin="nested"`` is that an inner join eager load chained to an outer
-join eager load will use a right-nested join. ``"nested"`` is now implied
-when using ``innerjoin=True``::
-
- query(User).options(
- joinedload("orders", innerjoin=False).joinedload("items", innerjoin=True)
- )
-
-With the new default, this will render the FROM clause in the form:\
-
-.. sourcecode:: text
-
- FROM users LEFT OUTER JOIN (orders JOIN items ON <onclause>) ON <onclause>
-
-That is, using a right-nested join for the INNER join so that the full
-result of ``users`` can be returned. The use of an INNER join is more efficient
-than using an OUTER join, and allows the :paramref:`_orm.joinedload.innerjoin`
-optimization parameter to take effect in all cases.
-
-To get the older behavior, use ``innerjoin="unnested"``::
-
- query(User).options(
- joinedload("orders", innerjoin=False).joinedload("items", innerjoin="unnested")
- )
-
-This will avoid right-nested joins and chain the joins together using all
-OUTER joins despite the innerjoin directive:
-
-.. sourcecode:: text
-
- FROM users LEFT OUTER JOIN orders ON <onclause> LEFT OUTER JOIN items ON <onclause>
-
-As noted in the 0.9 notes, the only database backend that has difficulty
-with right-nested joins is SQLite; SQLAlchemy as of 0.9 converts a right-nested
-join into a subquery as a join target on SQLite.
-
-.. seealso::
-
- :ref:`feature_2976` - description of the feature as introduced in 0.9.4.
-
-:ticket:`3008`
-
-.. _change_3249:
-
-Subqueries no longer applied to uselist=False joined eager loads
-----------------------------------------------------------------
-
-Given a joined eager load like the following::
-
- class A(Base):
- __tablename__ = "a"
- id = Column(Integer, primary_key=True)
- b = relationship("B", uselist=False)
-
-
- class B(Base):
- __tablename__ = "b"
- id = Column(Integer, primary_key=True)
- a_id = Column(ForeignKey("a.id"))
-
-
- s = Session()
- print(s.query(A).options(joinedload(A.b)).limit(5))
-
-SQLAlchemy considers the relationship ``A.b`` to be a "one to many,
-loaded as a single value", which is essentially a "one to one"
-relationship. However, joined eager loading has always treated the
-above as a situation where the main query needs to be inside a
-subquery, as would normally be needed for a collection of B objects
-where the main query has a LIMIT applied:
-
-.. sourcecode:: sql
-
- SELECT anon_1.a_id AS anon_1_a_id, b_1.id AS b_1_id, b_1.a_id AS b_1_a_id
- FROM (SELECT a.id AS a_id
- FROM a LIMIT :param_1) AS anon_1
- LEFT OUTER JOIN b AS b_1 ON anon_1.a_id = b_1.a_id
-
-However, since the relationship of the inner query to the outer one is
-that at most only one row is shared in the case of ``uselist=False``
-(in the same way as a many-to-one), the "subquery" used with LIMIT +
-joined eager loading is now dropped in this case:
-
-.. sourcecode:: sql
-
- SELECT a.id AS a_id, b_1.id AS b_1_id, b_1.a_id AS b_1_a_id
- FROM a LEFT OUTER JOIN b AS b_1 ON a.id = b_1.a_id
- LIMIT :param_1
-
-In the case that the LEFT OUTER JOIN returns more than one row, the ORM
-has always emitted a warning here and ignored additional results for
-``uselist=False``, so the results in that error situation should not change.
-
-:ticket:`3249`
-
-
-query.update() / query.delete() raises if used with join(), select_from(), from_self()
---------------------------------------------------------------------------------------
-
-A warning is emitted in SQLAlchemy 0.9.10 (not yet released as of
-June 9, 2015) when the :meth:`_query.Query.update` or :meth:`_query.Query.delete` methods
-are invoked against a query which has also called upon :meth:`_query.Query.join`,
-:meth:`_query.Query.outerjoin`,
-:meth:`_query.Query.select_from` or :meth:`_query.Query.from_self`. These are unsupported
-use cases which silently fail in the 0.9 series up until 0.9.10 where it emits
-a warning. In 1.0, these cases raise an exception.
-
-:ticket:`3349`
-
-
-query.update() with ``synchronize_session='evaluate'`` raises on multi-table update
------------------------------------------------------------------------------------
-
-The "evaluator" for :meth:`_query.Query.update` won't work with multi-table
-updates, and needs to be set to ``synchronize_session=False`` or
-``synchronize_session='fetch'`` when multiple tables are present.
-The new behavior is that an explicit exception is now raised, with a message
-to change the synchronize setting.
-This is upgraded from a warning emitted as of 0.9.7.
-
-:ticket:`3117`
-
-Resurrect Event has been Removed
---------------------------------
-
-The "resurrect" ORM event has been removed entirely. This event ceased to
-have any function since version 0.8 removed the older "mutable" system
-from the unit of work.
-
-
-.. _migration_3177:
-
-Change to single-table-inheritance criteria when using from_self(), count()
----------------------------------------------------------------------------
-
-Given a single-table inheritance mapping, such as::
-
- class Widget(Base):
- __table__ = "widget_table"
-
-
- class FooWidget(Widget):
- pass
-
-Using :meth:`_query.Query.from_self` or :meth:`_query.Query.count` against a subclass
-would produce a subquery, but then add the "WHERE" criteria for subtypes
-to the outside::
-
- sess.query(FooWidget).from_self().all()
-
-rendering:
-
-.. sourcecode:: sql
-
- SELECT
- anon_1.widgets_id AS anon_1_widgets_id,
- anon_1.widgets_type AS anon_1_widgets_type
- FROM (SELECT widgets.id AS widgets_id, widgets.type AS widgets_type,
- FROM widgets) AS anon_1
- WHERE anon_1.widgets_type IN (?)
-
-The issue with this is that if the inner query does not specify all
-columns, then we can't add the WHERE clause on the outside (it actually tries,
-and produces a bad query). This decision
-apparently goes way back to 0.6.5 with the note "may need to make more
-adjustments to this". Well, those adjustments have arrived! So now the
-above query will render:
-
-.. sourcecode:: sql
-
- SELECT
- anon_1.widgets_id AS anon_1_widgets_id,
- anon_1.widgets_type AS anon_1_widgets_type
- FROM (SELECT widgets.id AS widgets_id, widgets.type AS widgets_type,
- FROM widgets
- WHERE widgets.type IN (?)) AS anon_1
-
-So that queries that don't include "type" will still work!::
-
- sess.query(FooWidget.id).count()
-
-Renders:
-
-.. sourcecode:: sql
-
- SELECT count(*) AS count_1
- FROM (SELECT widgets.id AS widgets_id
- FROM widgets
- WHERE widgets.type IN (?)) AS anon_1
-
-
-:ticket:`3177`
-
-
-.. _migration_3222:
-
-
-single-table-inheritance criteria added to all ON clauses unconditionally
--------------------------------------------------------------------------
-
-When joining to a single-table inheritance subclass target, the ORM always adds
-the "single table criteria" when joining on a relationship. Given a
-mapping as::
-
- class Widget(Base):
- __tablename__ = "widget"
- id = Column(Integer, primary_key=True)
- type = Column(String)
- related_id = Column(ForeignKey("related.id"))
- related = relationship("Related", backref="widget")
- __mapper_args__ = {"polymorphic_on": type}
-
-
- class FooWidget(Widget):
- __mapper_args__ = {"polymorphic_identity": "foo"}
-
-
- class Related(Base):
- __tablename__ = "related"
- id = Column(Integer, primary_key=True)
-
-It's been the behavior for quite some time that a JOIN on the relationship
-will render a "single inheritance" clause for the type::
-
- s.query(Related).join(FooWidget, Related.widget).all()
-
-SQL output:
-
-.. sourcecode:: sql
-
- SELECT related.id AS related_id
- FROM related JOIN widget ON related.id = widget.related_id AND widget.type IN (:type_1)
-
-Above, because we joined to a subclass ``FooWidget``, :meth:`_query.Query.join`
-knew to add the ``AND widget.type IN ('foo')`` criteria to the ON clause.
-
-The change here is that the ``AND widget.type IN()`` criteria is now appended
-to *any* ON clause, not just those generated from a relationship,
-including one that is explicitly stated::
-
- # ON clause will now render as
- # related.id = widget.related_id AND widget.type IN (:type_1)
- s.query(Related).join(FooWidget, FooWidget.related_id == Related.id).all()
-
-As well as the "implicit" join when no ON clause of any kind is stated::
-
- # ON clause will now render as
- # related.id = widget.related_id AND widget.type IN (:type_1)
- s.query(Related).join(FooWidget).all()
-
-Previously, the ON clause for these would not include the single-inheritance
-criteria. Applications that are already adding this criteria to work around
-this will want to remove its explicit use, though it should continue to work
-fine if the criteria happens to be rendered twice in the meantime.
-
-.. seealso::
-
- :ref:`bug_3233`
-
-:ticket:`3222`
-
-Key Behavioral Changes - Core
-=============================
-
-.. _migration_2992:
-
-Warnings emitted when coercing full SQL fragments into text()
--------------------------------------------------------------
-
-Since SQLAlchemy's inception, there has always been an emphasis on not getting
-in the way of the usage of plain text. The Core and ORM expression systems
-were intended to allow any number of points at which the user can just
-use plain text SQL expressions, not just in the sense that you can send a
-full SQL string to :meth:`_engine.Connection.execute`, but that you can send strings
-with SQL expressions into many functions, such as :meth:`_expression.Select.where`,
-:meth:`_query.Query.filter`, and :meth:`_expression.Select.order_by`.
-
-Note that by "SQL expressions" we mean a **full fragment of a SQL string**,
-such as::
-
- # the argument sent to where() is a full SQL expression
- stmt = select([sometable]).where("somecolumn = 'value'")
-
-and we are **not talking about string arguments**, that is, the normal
-behavior of passing string values that become parameterized::
-
- # This is a normal Core expression with a string argument -
- # we aren't talking about this!!
- stmt = select([sometable]).where(sometable.c.somecolumn == "value")
-
-The Core tutorial has long featured an example of the use of this technique,
-using a :func:`_expression.select` construct where virtually all components of it
-are specified as straight strings. However, despite this long-standing
-behavior and example, users are apparently surprised that this behavior
-exists, and when asking around the community, I was unable to find any user
-that was in fact *not* surprised that you can send a full string into a method
-like :meth:`_query.Query.filter`.
-
-So the change here is to encourage the user to qualify textual strings when
-composing SQL that is partially or fully composed from textual fragments.
-When composing a select as below::
-
- stmt = select(["a", "b"]).where("a = b").select_from("sometable")
-
-The statement is built up normally, with all the same coercions as before.
-However, one will see the following warnings emitted:
-
-.. sourcecode:: text
-
- SAWarning: Textual column expression 'a' should be explicitly declared
- with text('a'), or use column('a') for more specificity
- (this warning may be suppressed after 10 occurrences)
-
- SAWarning: Textual column expression 'b' should be explicitly declared
- with text('b'), or use column('b') for more specificity
- (this warning may be suppressed after 10 occurrences)
-
- SAWarning: Textual SQL expression 'a = b' should be explicitly declared
- as text('a = b') (this warning may be suppressed after 10 occurrences)
-
- SAWarning: Textual SQL FROM expression 'sometable' should be explicitly
- declared as text('sometable'), or use table('sometable') for more
- specificity (this warning may be suppressed after 10 occurrences)
-
-These warnings attempt to show exactly where the issue is by displaying
-the parameters as well as where the string was received.
-The warnings make use of the :ref:`feature_3178` so that parameterized warnings
-can be emitted safely without running out of memory, and as always, if
-one wishes the warnings to be exceptions, the
-`Python Warnings Filter <https://docs.python.org/2/library/warnings.html>`_
-should be used::
-
- import warnings
-
- warnings.simplefilter("error") # all warnings raise an exception
-
-Given the above warnings, our statement works just fine, but
-to get rid of the warnings we would rewrite our statement as follows::
-
- from sqlalchemy import select, text
-
- stmt = (
- select([text("a"), text("b")]).where(text("a = b")).select_from(text("sometable"))
- )
-
-and as the warnings suggest, we can give our statement more specificity
-about the text if we use :func:`_expression.column` and :func:`.table`::
-
- from sqlalchemy import select, text, column, table
-
- stmt = (
- select([column("a"), column("b")])
- .where(text("a = b"))
- .select_from(table("sometable"))
- )
-
-Where note also that :func:`.table` and :func:`_expression.column` can now
-be imported from "sqlalchemy" without the "sql" part.
-
-The behavior here applies to :func:`_expression.select` as well as to key methods
-on :class:`_query.Query`, including :meth:`_query.Query.filter`,
-:meth:`_query.Query.from_statement` and :meth:`_query.Query.having`.
-
-ORDER BY and GROUP BY are special cases
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-There is one case where usage of a string has special meaning, and as part
-of this change we have enhanced its functionality. When we have a
-:func:`_expression.select` or :class:`_query.Query` that refers to some column name or named
-label, we might want to GROUP BY and/or ORDER BY known columns or labels::
-
- stmt = (
- select([user.c.name, func.count(user.c.id).label("id_count")])
- .group_by("name")
- .order_by("id_count")
- )
-
-In the above statement we expect to see "ORDER BY id_count", as opposed to a
-re-statement of the function. The string argument given is actively
-matched to an entry in the columns clause during compilation, so the above
-statement would produce as we expect, without warnings (though note that
-the ``"name"`` expression has been resolved to ``users.name``!):
-
-.. sourcecode:: sql
-
- SELECT users.name, count(users.id) AS id_count
- FROM users GROUP BY users.name ORDER BY id_count
-
-However, if we refer to a name that cannot be located, then we get
-the warning again, as below::
-
- stmt = select([user.c.name, func.count(user.c.id).label("id_count")]).order_by(
- "some_label"
- )
-
-The output does what we say, but again it warns us:
-
-.. sourcecode:: text
-
- SAWarning: Can't resolve label reference 'some_label'; converting to
- text() (this warning may be suppressed after 10 occurrences)
-
-.. sourcecode:: sql
-
- SELECT users.name, count(users.id) AS id_count
- FROM users ORDER BY some_label
-
-The above behavior applies to all those places where we might want to refer
-to a so-called "label reference"; ORDER BY and GROUP BY, but also within an
-OVER clause as well as a DISTINCT ON clause that refers to columns (e.g. the
-PostgreSQL syntax).
-
-We can still specify any arbitrary expression for ORDER BY or others using
-:func:`_expression.text`::
-
- stmt = select([users]).order_by(text("some special expression"))
-
-The upshot of the whole change is that SQLAlchemy now would like us
-to tell it when a string is sent that this string is explicitly
-a :func:`_expression.text` construct, or a column, table, etc., and if we use it as a
-label name in an order by, group by, or other expression, SQLAlchemy expects
-that the string resolves to something known, else it should again
-be qualified with :func:`_expression.text` or similar.
-
-:ticket:`2992`
-
-.. _bug_3288:
-
-Python-side defaults invoked for each row individually when using a multivalued insert
---------------------------------------------------------------------------------------
-
-Support for Python-side column defaults when using the multi-valued
-version of :meth:`_expression.Insert.values` were essentially not implemented, and
-would only work "by accident" in specific situations, when the dialect in
-use was using a non-positional (e.g. named) style of bound parameter, and
-when it was not necessary that a Python-side callable be invoked for each
-row.
-
-The feature has been overhauled so that it works more similarly to
-that of an "executemany" style of invocation::
-
- import itertools
-
- counter = itertools.count(1)
- t = Table(
- "my_table",
- metadata,
- Column("id", Integer, default=lambda: next(counter)),
- Column("data", String),
- )
-
- conn.execute(
- t.insert().values(
- [
- {"data": "d1"},
- {"data": "d2"},
- {"data": "d3"},
- ]
- )
- )
-
-The above example will invoke ``next(counter)`` for each row individually
-as would be expected:
-
-.. sourcecode:: sql
-
- INSERT INTO my_table (id, data) VALUES (?, ?), (?, ?), (?, ?)
- (1, 'd1', 2, 'd2', 3, 'd3')
-
-Previously, a positional dialect would fail as a bind would not be generated
-for additional positions:
-
-.. sourcecode:: text
-
- Incorrect number of bindings supplied. The current statement uses 6,
- and there are 4 supplied.
- [SQL: u'INSERT INTO my_table (id, data) VALUES (?, ?), (?, ?), (?, ?)']
- [parameters: (1, 'd1', 'd2', 'd3')]
-
-And with a "named" dialect, the same value for "id" would be re-used in
-each row (hence this change is backwards-incompatible with a system that
-relied on this):
-
-.. sourcecode:: sql
-
- INSERT INTO my_table (id, data) VALUES (:id, :data_0), (:id, :data_1), (:id, :data_2)
- -- {u'data_2': 'd3', u'data_1': 'd2', u'data_0': 'd1', 'id': 1}
-
-The system will also refuse to invoke a "server side" default as inline-rendered
-SQL, since it cannot be guaranteed that a server side default is compatible
-with this. If the VALUES clause renders for a specific column, then a Python-side
-value is required; if an omitted value only refers to a server-side default,
-an exception is raised::
-
- t = Table(
- "my_table",
- metadata,
- Column("id", Integer, primary_key=True),
- Column("data", String, server_default="some default"),
- )
-
- conn.execute(
- t.insert().values(
- [
- {"data": "d1"},
- {"data": "d2"},
- {},
- ]
- )
- )
-
-will raise:
-
-.. sourcecode:: text
-
- sqlalchemy.exc.CompileError: INSERT value for column my_table.data is
- explicitly rendered as a boundparameter in the VALUES clause; a
- Python-side value or SQL expression is required
-
-Previously, the value "d1" would be copied into that of the third
-row (but again, only with named format!):
-
-.. sourcecode:: sql
-
- INSERT INTO my_table (data) VALUES (:data_0), (:data_1), (:data_0)
- -- {u'data_1': 'd2', u'data_0': 'd1'}
-
-:ticket:`3288`
-
-.. _change_3163:
-
-Event listeners can not be added or removed from within that event's runner
----------------------------------------------------------------------------
-
-Removal of an event listener from inside that same event itself would
-modify the elements of a list during iteration, which would cause
-still-attached event listeners to silently fail to fire. To prevent
-this while still maintaining performance, the lists have been replaced
-with ``collections.deque()``, which does not allow any additions or
-removals during iteration, and instead raises ``RuntimeError``.
-
-:ticket:`3163`
-
-.. _change_3169:
-
-The INSERT...FROM SELECT construct now implies ``inline=True``
---------------------------------------------------------------
-
-Using :meth:`_expression.Insert.from_select` now implies ``inline=True``
-on :func:`_expression.insert`. This helps to fix a bug where an
-INSERT...FROM SELECT construct would inadvertently be compiled
-as "implicit returning" on supporting backends, which would
-cause breakage in the case of an INSERT that inserts zero rows
-(as implicit returning expects a row), as well as arbitrary
-return data in the case of an INSERT that inserts multiple
-rows (e.g. only the first row of many).
-A similar change is also applied to an INSERT..VALUES
-with multiple parameter sets; implicit RETURNING will no longer emit
-for this statement either. As both of these constructs deal
-with variable numbers of rows, the
-:attr:`_engine.ResultProxy.inserted_primary_key` accessor does not
-apply. Previously, there was a documentation note that one
-may prefer ``inline=True`` with INSERT..FROM SELECT as some databases
-don't support returning and therefore can't do "implicit" returning,
-but there's no reason an INSERT...FROM SELECT needs implicit returning
-in any case. Regular explicit :meth:`_expression.Insert.returning` should
-be used to return variable numbers of result rows if inserted
-data is needed.
-
-:ticket:`3169`
-
-.. _change_3027:
-
-``autoload_with`` now implies ``autoload=True``
------------------------------------------------
-
-A :class:`_schema.Table` can be set up for reflection by passing
-:paramref:`_schema.Table.autoload_with` alone::
-
- my_table = Table("my_table", metadata, autoload_with=some_engine)
-
-:ticket:`3027`
-
-.. _change_3266:
-
-DBAPI exception wrapping and handle_error() event improvements
---------------------------------------------------------------
-
-SQLAlchemy's wrapping of DBAPI exceptions was not taking place in the
-case where a :class:`_engine.Connection` object was invalidated, and then tried
-to reconnect and encountered an error; this has been resolved.
-
-Additionally, the recently added :meth:`_events.ConnectionEvents.handle_error`
-event is now invoked for errors that occur upon initial connect, upon
-reconnect, and when :func:`_sa.create_engine` is used given a custom connection
-function via :paramref:`_sa.create_engine.creator`.
-
-The :class:`.ExceptionContext` object has a new datamember
-:attr:`.ExceptionContext.engine` that will always refer to the :class:`_engine.Engine`
-in use, in those cases when the :class:`_engine.Connection` object is not available
-(e.g. on initial connect).
-
-
-:ticket:`3266`
-
-.. _change_3243:
-
-ForeignKeyConstraint.columns is now a ColumnCollection
-------------------------------------------------------
-
-:attr:`_schema.ForeignKeyConstraint.columns` was previously a plain list
-containing either strings or :class:`_schema.Column` objects, depending on
-how the :class:`_schema.ForeignKeyConstraint` was constructed and whether it was
-associated with a table. The collection is now a :class:`_expression.ColumnCollection`,
-and is only initialized after the :class:`_schema.ForeignKeyConstraint` is
-associated with a :class:`_schema.Table`. A new accessor
-:attr:`_schema.ForeignKeyConstraint.column_keys`
-is added to unconditionally return string keys for the local set of
-columns regardless of how the object was constructed or its current
-state.
-
-
-.. _feature_3084:
-
-MetaData.sorted_tables accessor is "deterministic"
---------------------------------------------------
-
-The sorting of tables resulting from the :attr:`_schema.MetaData.sorted_tables`
-accessor is "deterministic"; the ordering should be the same in all cases
-regardless of Python hashing. This is done by first sorting the tables
-by name before passing them to the topological algorithm, which maintains
-that ordering as it iterates.
-
-Note that this change does **not** yet apply to the ordering applied
-when emitting :meth:`_schema.MetaData.create_all` or :meth:`_schema.MetaData.drop_all`.
-
-:ticket:`3084`
-
-.. _bug_3170:
-
-null(), false() and true() constants are no longer singletons
--------------------------------------------------------------
-
-These three constants were changed to return a "singleton" value
-in 0.9; unfortunately, that would lead to a query like the following
-to not render as expected::
-
- select([null(), null()])
-
-rendering only ``SELECT NULL AS anon_1``, because the two :func:`.null`
-constructs would come out as the same ``NULL`` object, and
-SQLAlchemy's Core model is based on object identity in order to
-determine lexical significance. The change in 0.9 had no
-importance other than the desire to save on object overhead; in general,
-an unnamed construct needs to stay lexically unique so that it gets
-labeled uniquely.
-
-:ticket:`3170`
-
-.. _change_3204:
-
-SQLite/Oracle have distinct methods for temporary table/view name reporting
----------------------------------------------------------------------------
-
-The :meth:`_reflection.Inspector.get_table_names` and :meth:`_reflection.Inspector.get_view_names`
-methods in the case of SQLite/Oracle would also return the names of temporary
-tables and views, which is not provided by any other dialect (in the case
-of MySQL at least it is not even possible). This logic has been moved
-out to two new methods :meth:`_reflection.Inspector.get_temp_table_names` and
-:meth:`_reflection.Inspector.get_temp_view_names`.
-
-Note that reflection of a specific named temporary table or temporary view,
-either by ``Table('name', autoload=True)`` or via methods like
-:meth:`_reflection.Inspector.get_columns` continues to function for most if not all
-dialects. For SQLite specifically, there is a bug fix for UNIQUE constraint
-reflection from temp tables as well, which is :ticket:`3203`.
-
-:ticket:`3204`
-
-Dialect Improvements and Changes - PostgreSQL
-=============================================
-
-.. _change_3319:
-
-Overhaul of ENUM type create/drop rules
----------------------------------------
-
-The rules for PostgreSQL :class:`_postgresql.ENUM` have been made more strict
-with regards to creating and dropping of the TYPE.
-
-An :class:`_postgresql.ENUM` that is created **without** being explicitly
-associated with a :class:`_schema.MetaData` object will be created *and* dropped
-corresponding to :meth:`_schema.Table.create` and :meth:`_schema.Table.drop`::
-
- table = Table(
- "sometable", metadata, Column("some_enum", ENUM("a", "b", "c", name="myenum"))
- )
-
- table.create(engine) # will emit CREATE TYPE and CREATE TABLE
- table.drop(engine) # will emit DROP TABLE and DROP TYPE - new for 1.0
-
-This means that if a second table also has an enum named 'myenum', the
-above DROP operation will now fail. In order to accommodate the use case
-of a common shared enumerated type, the behavior of a metadata-associated
-enumeration has been enhanced.
-
-An :class:`_postgresql.ENUM` that is created **with** being explicitly
-associated with a :class:`_schema.MetaData` object will *not* be created *or* dropped
-corresponding to :meth:`_schema.Table.create` and :meth:`_schema.Table.drop`, with
-the exception of :meth:`_schema.Table.create` called with the ``checkfirst=True``
-flag::
-
- my_enum = ENUM("a", "b", "c", name="myenum", metadata=metadata)
-
- table = Table("sometable", metadata, Column("some_enum", my_enum))
-
- # will fail: ENUM 'my_enum' does not exist
- table.create(engine)
-
- # will check for enum and emit CREATE TYPE
- table.create(engine, checkfirst=True)
-
- table.drop(engine) # will emit DROP TABLE, *not* DROP TYPE
-
- metadata.drop_all(engine) # will emit DROP TYPE
-
- metadata.create_all(engine) # will emit CREATE TYPE
-
-:ticket:`3319`
-
-New PostgreSQL Table options
-----------------------------
-
-Added support for PG table options TABLESPACE, ON COMMIT,
-WITH(OUT) OIDS, and INHERITS, when rendering DDL via
-the :class:`_schema.Table` construct.
-
-.. seealso::
-
- :ref:`postgresql_table_options`
-
-:ticket:`2051`
-
-.. _feature_get_enums:
-
-New get_enums() method with PostgreSQL Dialect
-----------------------------------------------
-
-The :func:`_sa.inspect` method returns a :class:`.PGInspector` object in the
-case of PostgreSQL, which includes a new :meth:`.PGInspector.get_enums`
-method that returns information on all available ``ENUM`` types::
-
- from sqlalchemy import inspect, create_engine
-
- engine = create_engine("postgresql+psycopg2://host/dbname")
- insp = inspect(engine)
- print(insp.get_enums())
-
-.. seealso::
-
- :meth:`.PGInspector.get_enums`
-
-.. _feature_2891:
-
-PostgreSQL Dialect reflects Materialized Views, Foreign Tables
---------------------------------------------------------------
-
-Changes are as follows:
-
-* the :class:`Table` construct with ``autoload=True`` will now match a name
- that exists in the database as a materialized view or foreign table.
-
-* :meth:`_reflection.Inspector.get_view_names` will return plain and materialized view
- names.
-
-* :meth:`_reflection.Inspector.get_table_names` does **not** change for PostgreSQL, it
- continues to return only the names of plain tables.
-
-* A new method :meth:`.PGInspector.get_foreign_table_names` is added which
- will return the names of tables that are specifically marked as "foreign"
- in the PostgreSQL schema tables.
-
-The change to reflection involves adding ``'m'`` and ``'f'`` to the list
-of qualifiers we use when querying ``pg_class.relkind``, but this change
-is new in 1.0.0 to avoid any backwards-incompatible surprises for those
-running 0.9 in production.
-
-:ticket:`2891`
-
-.. _change_3264:
-
-PostgreSQL ``has_table()`` now works for temporary tables
----------------------------------------------------------
-
-This is a simple fix such that "has table" for temporary tables now works,
-so that code like the following may proceed::
-
- from sqlalchemy import *
-
- metadata = MetaData()
- user_tmp = Table(
- "user_tmp",
- metadata,
- Column("id", INT, primary_key=True),
- Column("name", VARCHAR(50)),
- prefixes=["TEMPORARY"],
- )
-
- e = create_engine("postgresql://scott:tiger@localhost/test", echo="debug")
- with e.begin() as conn:
- user_tmp.create(conn, checkfirst=True)
-
- # checkfirst will succeed
- user_tmp.create(conn, checkfirst=True)
-
-The very unlikely case that this behavior will cause a non-failing application
-to behave differently, is because PostgreSQL allows a non-temporary table
-to silently overwrite a temporary table. So code like the following will
-now act completely differently, no longer creating the real table following
-the temporary table::
-
- from sqlalchemy import *
-
- metadata = MetaData()
- user_tmp = Table(
- "user_tmp",
- metadata,
- Column("id", INT, primary_key=True),
- Column("name", VARCHAR(50)),
- prefixes=["TEMPORARY"],
- )
-
- e = create_engine("postgresql://scott:tiger@localhost/test", echo="debug")
- with e.begin() as conn:
- user_tmp.create(conn, checkfirst=True)
-
- m2 = MetaData()
- user = Table(
- "user_tmp",
- m2,
- Column("id", INT, primary_key=True),
- Column("name", VARCHAR(50)),
- )
-
- # in 0.9, *will create* the new table, overwriting the old one.
- # in 1.0, *will not create* the new table
- user.create(conn, checkfirst=True)
-
-:ticket:`3264`
-
-.. _feature_gh134:
-
-PostgreSQL FILTER keyword
--------------------------
-
-The SQL standard FILTER keyword for aggregate functions is now supported
-by PostgreSQL as of 9.4. SQLAlchemy allows this using
-:meth:`.FunctionElement.filter`::
-
- func.count(1).filter(True)
-
-.. seealso::
-
- :meth:`.FunctionElement.filter`
-
- :class:`.FunctionFilter`
-
-PG8000 dialect supports client side encoding
---------------------------------------------
-
-The :paramref:`_sa.create_engine.encoding` parameter is now honored
-by the pg8000 dialect, using on connect handler which
-emits ``SET CLIENT_ENCODING`` matching the selected encoding.
-
-PG8000 native JSONB support
----------------------------
-
-Support for PG8000 versions greater than 1.10.1 has been added, where
-JSONB is supported natively.
-
-
-Support for psycopg2cffi Dialect on PyPy
-----------------------------------------
-
-Support for the pypy psycopg2cffi dialect is added.
-
-.. seealso::
-
- :mod:`sqlalchemy.dialects.postgresql.psycopg2cffi`
-
-Dialect Improvements and Changes - MySQL
-========================================
-
-.. _change_3155:
-
-MySQL TIMESTAMP Type now renders NULL / NOT NULL in all cases
--------------------------------------------------------------
-
-The MySQL dialect has always worked around MySQL's implicit NOT NULL
-default associated with TIMESTAMP columns by emitting NULL for
-such a type, if the column is set up with ``nullable=True``. However,
-MySQL 5.6.6 and above features a new flag
-`explicit_defaults_for_timestamp <https://dev.mysql.com/doc/refman/
-5.6/en/server-system-variables.html
-#sysvar_explicit_defaults_for_timestamp>`_ which repairs MySQL's non-standard
-behavior to make it behave like any other type; to accommodate this,
-SQLAlchemy now emits NULL/NOT NULL unconditionally for all TIMESTAMP
-columns.
-
-.. seealso::
-
- :ref:`mysql_timestamp_null`
-
-:ticket:`3155`
-
-
-.. _change_3283:
-
-MySQL SET Type Overhauled to support empty sets, unicode, blank value handling
-------------------------------------------------------------------------------
-
-The :class:`.mysql.SET` type historically not included a system of handling
-blank sets and empty values separately; as different drivers had different
-behaviors for treatment of empty strings and empty-string-set representations,
-the SET type tried only to hedge between these behaviors, opting to treat the
-empty set as ``set([''])`` as is still the current behavior for the
-MySQL-Connector-Python DBAPI.
-Part of the rationale here was that it was otherwise impossible to actually
-store a blank string within a MySQL SET, as the driver gives us back strings
-with no way to discern between ``set([''])`` and ``set()``. It was left
-to the user to determine if ``set([''])`` actually meant "empty set" or not.
-
-The new behavior moves the use case for the blank string, which is an unusual
-case that isn't even documented in MySQL's documentation, into a special
-case, and the default behavior of :class:`.mysql.SET` is now:
-
-* to treat the empty string ``''`` as returned by MySQL-python into the empty
- set ``set()``;
-
-* to convert the single-blank value set ``set([''])`` returned by
- MySQL-Connector-Python into the empty set ``set()``;
-
-* To handle the case of a set type that actually wishes includes the blank
- value ``''`` in its list of possible values,
- a new feature (required in this use case) is implemented whereby the set
- value is persisted and loaded as a bitwise integer value; the
- flag :paramref:`.mysql.SET.retrieve_as_bitwise` is added in order to
- enable this.
-
-Using the :paramref:`.mysql.SET.retrieve_as_bitwise` flag allows the set
-to be persisted and retrieved with no ambiguity of values. Theoretically
-this flag can be turned on in all cases, as long as the given list of
-values to the type matches the ordering exactly as declared in the
-database; it only makes the SQL echo output a bit more unusual.
-
-The default behavior of :class:`.mysql.SET` otherwise remains the same,
-roundtripping values using strings. The string-based behavior now
-supports unicode fully including MySQL-python with use_unicode=0.
-
-:ticket:`3283`
-
-
-MySQL internal "no such table" exceptions not passed to event handlers
-----------------------------------------------------------------------
-
-The MySQL dialect will now disable :meth:`_events.ConnectionEvents.handle_error`
-events from firing for those statements which it uses internally
-to detect if a table exists or not. This is achieved using an
-execution option ``skip_user_error_events`` that disables the handle
-error event for the scope of that execution. In this way, user code
-that rewrites exceptions doesn't need to worry about the MySQL
-dialect or other dialects that occasionally need to catch
-SQLAlchemy specific exceptions.
-
-
-Changed the default value of ``raise_on_warnings`` for MySQL-Connector
-----------------------------------------------------------------------
-
-Changed the default value of "raise_on_warnings" to False for
-MySQL-Connector. This was set at True for some reason. The "buffered"
-flag unfortunately must stay at True as MySQLconnector does not allow
-a cursor to be closed unless all results are fully fetched.
-
-:ticket:`2515`
-
-.. _bug_3186:
-
-MySQL boolean symbols "true", "false" work again
-------------------------------------------------
-
-0.9's overhaul of the IS/IS NOT operators as well as boolean types in
-:ticket:`2682` disallowed the MySQL dialect from making use of the
-"true" and "false" symbols in the context of "IS" / "IS NOT". Apparently,
-even though MySQL has no "boolean" type, it supports IS / IS NOT when the
-special "true" and "false" symbols are used, even though these are otherwise
-synonymous with "1" and "0" (and IS/IS NOT don't work with the numerics).
-
-So the change here is that the MySQL dialect remains "non native boolean",
-but the :func:`.true` and :func:`.false` symbols again produce the
-keywords "true" and "false", so that an expression like ``column.is_(true())``
-again works on MySQL.
-
-:ticket:`3186`
-
-.. _change_3263:
-
-The match() operator now returns an agnostic MatchType compatible with MySQL's floating point return value
-----------------------------------------------------------------------------------------------------------
-
-The return type of a :meth:`.ColumnOperators.match` expression is now a new type
-called :class:`.MatchType`. This is a subclass of :class:`.Boolean`,
-that can be intercepted by the dialect in order to produce a different
-result type at SQL execution time.
-
-Code like the following will now function correctly and return floating points
-on MySQL::
-
- >>> connection.execute(
- ... select(
- ... [
- ... matchtable.c.title.match("Agile Ruby Programming").label("ruby"),
- ... matchtable.c.title.match("Dive Python").label("python"),
- ... matchtable.c.title,
- ... ]
- ... ).order_by(matchtable.c.id)
- ... )
- [
- (2.0, 0.0, 'Agile Web Development with Ruby On Rails'),
- (0.0, 2.0, 'Dive Into Python'),
- (2.0, 0.0, "Programming Matz's Ruby"),
- (0.0, 0.0, 'The Definitive Guide to Django'),
- (0.0, 1.0, 'Python in a Nutshell')
- ]
-
-
-:ticket:`3263`
-
-.. _change_2984:
-
-Drizzle Dialect is now an External Dialect
-------------------------------------------
-
-The dialect for `Drizzle <https://www.drizzle.org/>`_ is now an external
-dialect, available at https://bitbucket.org/zzzeek/sqlalchemy-drizzle.
-This dialect was added to SQLAlchemy right before SQLAlchemy was able to
-accommodate third party dialects well; going forward, all databases that aren't
-within the "ubiquitous use" category are third party dialects.
-The dialect's implementation hasn't changed and is still based on the
-MySQL + MySQLdb dialects within SQLAlchemy. The dialect is as of yet
-unreleased and in "attic" status; however it passes the majority of tests
-and is generally in decent working order, if someone wants to pick up
-on polishing it.
-
-Dialect Improvements and Changes - SQLite
-=========================================
-
-SQLite named and unnamed UNIQUE and FOREIGN KEY constraints will inspect and reflect
--------------------------------------------------------------------------------------
-
-UNIQUE and FOREIGN KEY constraints are now fully reflected on
-SQLite both with and without names. Previously, foreign key
-names were ignored and unnamed unique constraints were skipped. In particular
-this will help with Alembic's new SQLite migration features.
-
-To achieve this, for both foreign keys and unique constraints, the result
-of PRAGMA foreign_keys, index_list, and index_info is combined with regular
-expression parsing of the CREATE TABLE statement overall to form a complete
-picture of the names of constraints, as well as differentiating UNIQUE
-constraints that were created as UNIQUE vs. unnamed INDEXes.
-
-:ticket:`3244`
-
-:ticket:`3261`
-
-Dialect Improvements and Changes - SQL Server
-=============================================
-
-.. _change_3182:
-
-PyODBC driver name is required with hostname-based SQL Server connections
--------------------------------------------------------------------------
-
-Connecting to SQL Server with PyODBC using a DSN-less connection, e.g.
-with an explicit hostname, now requires a driver name - SQLAlchemy will no
-longer attempt to guess a default::
-
- engine = create_engine(
- "mssql+pyodbc://scott:tiger@myhost:port/databasename?driver=SQL+Server+Native+Client+10.0"
- )
-
-SQLAlchemy's previously hardcoded default of "SQL Server" is obsolete on
-Windows, and SQLAlchemy cannot be tasked with guessing the best driver
-based on operation system/driver detection. Using a DSN is always preferred
-when using ODBC to avoid this issue entirely.
-
-:ticket:`3182`
-
-SQL Server 2012 large text / binary types render as VARCHAR, NVARCHAR, VARBINARY
---------------------------------------------------------------------------------
-
-The rendering of the :class:`_expression.TextClause`, :class:`.UnicodeText`, and :class:`.LargeBinary`
-types has been changed for SQL Server 2012 and greater, with options
-to control the behavior completely, based on deprecation guidelines from
-Microsoft. See :ref:`mssql_large_type_deprecation` for details.
-
-Dialect Improvements and Changes - Oracle
-=========================================
-
-.. _change_3220:
-
-Improved support for CTEs in Oracle
------------------------------------
-
-CTE support has been fixed up for Oracle, and there is also a new feature
-:meth:`_expression.CTE.with_suffixes` that can assist with Oracle's special directives::
-
- included_parts = (
- select([part.c.sub_part, part.c.part, part.c.quantity])
- .where(part.c.part == "p1")
- .cte(name="included_parts", recursive=True)
- .suffix_with(
- "search depth first by part set ord1",
- "cycle part set y_cycle to 1 default 0",
- dialect="oracle",
- )
- )
-
-:ticket:`3220`
-
-New Oracle Keywords for DDL
----------------------------
-
-Keywords such as COMPRESS, ON COMMIT, BITMAP:
-
-:ref:`oracle_table_options`
-
-:ref:`oracle_index_options`
+++ /dev/null
-=============================
-What's New in SQLAlchemy 1.1?
-=============================
-
-.. admonition:: About this Document
-
- This document describes changes between SQLAlchemy version 1.0
- and SQLAlchemy version 1.1.
-
-Introduction
-============
-
-This guide introduces what's new in SQLAlchemy version 1.1,
-and also documents changes which affect users migrating
-their applications from the 1.0 series of SQLAlchemy to 1.1.
-
-Please carefully review the sections on behavioral changes for
-potentially backwards-incompatible changes in behavior.
-
-Platform / Installer Changes
-============================
-
-Setuptools is now required for install
---------------------------------------
-
-SQLAlchemy's ``setup.py`` file has for many years supported operation
-both with Setuptools installed and without; supporting a "fallback" mode
-that uses straight Distutils. As a Setuptools-less Python environment is
-now unheard of, and in order to support the featureset of Setuptools
-more fully, in particular to support py.test's integration with it as well
-as things like "extras", ``setup.py`` now depends on Setuptools fully.
-
-.. seealso::
-
- :ref:`installation`
-
-:ticket:`3489`
-
-Enabling / Disabling C Extension builds is only via environment variable
-------------------------------------------------------------------------
-
-The C Extensions build by default during install as long as it is possible.
-To disable C extension builds, the ``DISABLE_SQLALCHEMY_CEXT`` environment
-variable was made available as of SQLAlchemy 0.8.6 / 0.9.4. The previous
-approach of using the ``--without-cextensions`` argument has been removed,
-as it relies on deprecated features of setuptools.
-
-.. seealso::
-
- :ref:`c_extensions`
-
-:ticket:`3500`
-
-
-New Features and Improvements - ORM
-===================================
-
-.. _change_2677:
-
-New Session lifecycle events
-----------------------------
-
-The :class:`.Session` has long supported events that allow some degree
-of tracking of state changes to objects, including
-:meth:`.SessionEvents.before_attach`, :meth:`.SessionEvents.after_attach`,
-and :meth:`.SessionEvents.before_flush`. The Session documentation also
-documents major object states at :ref:`session_object_states`. However,
-there has never been system of tracking objects specifically as they
-pass through these transitions. Additionally, the status of "deleted" objects
-has historically been murky as the objects act somewhere between
-the "persistent" and "detached" states.
-
-To clean up this area and allow the realm of session state transition
-to be fully transparent, a new series of events have been added that
-are intended to cover every possible way that an object might transition
-between states, and additionally the "deleted" status has been given
-its own official state name within the realm of session object states.
-
-New State Transition Events
-^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Transitions between all states of an object such as :term:`persistent`,
-:term:`pending` and others can now be intercepted in terms of a
-session-level event intended to cover a specific transition.
-Transitions as objects move into a :class:`.Session`, move out of a
-:class:`.Session`, and even all the transitions which occur when the
-transaction is rolled back using :meth:`.Session.rollback`
-are explicitly present in the interface of :class:`.SessionEvents`.
-
-In total, there are **ten new events**. A summary of these events is in a
-newly written documentation section :ref:`session_lifecycle_events`.
-
-
-New Object State "deleted" is added, deleted objects no longer "persistent"
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The :term:`persistent` state of an object in the :class:`.Session` has
-always been documented as an object that has a valid database identity;
-however in the case of objects that were deleted within a flush, they
-have always been in a grey area where they are not really "detached"
-from the :class:`.Session` yet, because they can still be restored
-within a rollback, but are not really "persistent" because their database
-identity has been deleted and they aren't present in the identity map.
-
-To resolve this grey area given the new events, a new object state
-:term:`deleted` is introduced. This state exists between the "persistent" and
-"detached" states. An object that is marked for deletion via
-:meth:`.Session.delete` remains in the "persistent" state until a flush
-proceeds; at that point, it is removed from the identity map, moves
-to the "deleted" state, and the :meth:`.SessionEvents.persistent_to_deleted`
-hook is invoked. If the :class:`.Session` object's transaction is rolled
-back, the object is restored as persistent; the
-:meth:`.SessionEvents.deleted_to_persistent` transition is called. Otherwise
-if the :class:`.Session` object's transaction is committed,
-the :meth:`.SessionEvents.deleted_to_detached` transition is invoked.
-
-Additionally, the :attr:`.InstanceState.persistent` accessor **no longer returns
-True** for an object that is in the new "deleted" state; instead, the
-:attr:`.InstanceState.deleted` accessor has been enhanced to reliably
-report on this new state. When the object is detached, the :attr:`.InstanceState.deleted`
-returns False and the :attr:`.InstanceState.detached` accessor is True
-instead. To determine if an object was deleted either in the current
-transaction or in a previous transaction, use the
-:attr:`.InstanceState.was_deleted` accessor.
-
-Strong Identity Map is Deprecated
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-One of the inspirations for the new series of transition events was to enable
-leak-proof tracking of objects as they move in and out of the identity map,
-so that a "strong reference" may be maintained mirroring the object
-moving in and out of this map. With this new capability, there is no longer
-any need for the :paramref:`.Session.weak_identity_map` parameter and the
-corresponding :class:`.StrongIdentityMap` object. This option has remained
-in SQLAlchemy for many years as the "strong-referencing" behavior used to be
-the only behavior available, and many applications were written to assume
-this behavior. It has long been recommended that strong-reference tracking
-of objects not be an intrinsic job of the :class:`.Session` and instead
-be an application-level construct built as needed by the application; the
-new event model allows even the exact behavior of the strong identity map
-to be replicated. See :ref:`session_referencing_behavior` for a new
-recipe illustrating how to replace the strong identity map.
-
-:ticket:`2677`
-
-.. _change_1311:
-
-New init_scalar() event intercepts default values at ORM level
---------------------------------------------------------------
-
-The ORM produces a value of ``None`` when an attribute that has not been
-set is first accessed, for a non-persistent object::
-
- >>> obj = MyObj()
- >>> obj.some_value
- None
-
-There's a use case for this in-Python value to correspond to that of a
-Core-generated default value, even before the object is persisted.
-To suit this use case a new event :meth:`.AttributeEvents.init_scalar`
-is added. The new example ``active_column_defaults.py`` at
-:ref:`examples_instrumentation` illustrates a sample use, so the effect
-can instead be::
-
- >>> obj = MyObj()
- >>> obj.some_value
- "my default"
-
-:ticket:`1311`
-
-.. _change_3499:
-
-Changes regarding "unhashable" types, impacts deduping of ORM rows
-------------------------------------------------------------------
-
-The :class:`_query.Query` object has a well-known behavior of "deduping"
-returned rows that contain at least one ORM-mapped entity (e.g., a
-full mapped object, as opposed to individual column values). The
-primary purpose of this is so that the handling of entities works
-smoothly in conjunction with the identity map, including to
-accommodate for the duplicate entities normally represented within
-joined eager loading, as well as when joins are used for the purposes
-of filtering on additional columns.
-
-This deduplication relies upon the hashability of the elements within
-the row. With the introduction of PostgreSQL's special types like
-:class:`_postgresql.ARRAY`, :class:`_postgresql.HSTORE` and
-:class:`_postgresql.JSON`, the experience of types within rows being
-unhashable and encountering problems here is more prevalent than
-it was previously.
-
-In fact, SQLAlchemy has since version 0.8 included a flag on datatypes that
-are noted as "unhashable", however this flag was not used consistently
-on built in types. As described in :ref:`change_3499_postgresql`, this
-flag is now set consistently for all of PostgreSQL's "structural" types.
-
-The "unhashable" flag is also set on the :class:`.NullType` type,
-as :class:`.NullType` is used to refer to any expression of unknown
-type.
-
-Since :class:`.NullType` is applied to most
-usages of :attr:`.func`, as :attr:`.func` doesn't actually know anything
-about the function names given in most cases, **using func() will
-often disable row deduping unless explicit typing is applied**.
-The following examples illustrate ``func.substr()`` applied to a string
-expression, and ``func.date()`` applied to a datetime expression; both
-examples will return duplicate rows due to the joined eager load unless
-explicit typing is applied::
-
- result = (
- session.query(func.substr(A.some_thing, 0, 4), A).options(joinedload(A.bs)).all()
- )
-
- users = (
- session.query(
- func.date(User.date_created, "start of month").label("month"),
- User,
- )
- .options(joinedload(User.orders))
- .all()
- )
-
-The above examples, in order to retain deduping, should be specified as::
-
- result = (
- session.query(func.substr(A.some_thing, 0, 4, type_=String), A)
- .options(joinedload(A.bs))
- .all()
- )
-
- users = (
- session.query(
- func.date(User.date_created, "start of month", type_=DateTime).label("month"),
- User,
- )
- .options(joinedload(User.orders))
- .all()
- )
-
-Additionally, the treatment of a so-called "unhashable" type is slightly
-different than its been in previous releases; internally we are using
-the ``id()`` function to get a "hash value" from these structures, just
-as we would any ordinary mapped object. This replaces the previous
-approach which applied a counter to the object.
-
-:ticket:`3499`
-
-.. _change_3321:
-
-Specific checks added for passing mapped classes, instances as SQL literals
----------------------------------------------------------------------------
-
-The typing system now has specific checks for passing of SQLAlchemy
-"inspectable" objects in contexts where they would otherwise be handled as
-literal values. Any SQLAlchemy built-in object that is legal to pass as a
-SQL value (which is not already a :class:`_expression.ClauseElement` instance)
-includes a method ``__clause_element__()`` which provides a
-valid SQL expression for that object. For SQLAlchemy objects that
-don't provide this, such as mapped classes, mappers, and mapped
-instances, a more informative error message is emitted rather than
-allowing the DBAPI to receive the object and fail later. An example
-is illustrated below, where a string-based attribute ``User.name`` is
-compared to a full instance of ``User()``, rather than against a
-string value::
-
- >>> some_user = User()
- >>> q = s.query(User).filter(User.name == some_user)
- sqlalchemy.exc.ArgumentError: Object <__main__.User object at 0x103167e90> is not legal as a SQL literal value
-
-The exception is now immediate when the comparison is made between
-``User.name == some_user``. Previously, a comparison like the above
-would produce a SQL expression that would only fail once resolved
-into a DBAPI execution call; the mapped ``User`` object would
-ultimately become a bound parameter that would be rejected by the
-DBAPI.
-
-Note that in the above example, the expression fails because
-``User.name`` is a string-based (e.g. column oriented) attribute.
-The change does *not* impact the usual case of comparing a many-to-one
-relationship attribute to an object, which is handled distinctly::
-
- >>> # Address.user refers to the User mapper, so
- >>> # this is of course still OK!
- >>> q = s.query(Address).filter(Address.user == some_user)
-
-
-:ticket:`3321`
-
-.. _feature_indexable:
-
-New Indexable ORM extension
----------------------------
-
-The :ref:`indexable_toplevel` extension is an extension to the hybrid
-attribute feature which allows the construction of attributes which
-refer to specific elements of an "indexable" data type, such as an array
-or JSON field::
-
- class Person(Base):
- __tablename__ = "person"
-
- id = Column(Integer, primary_key=True)
- data = Column(JSON)
-
- name = index_property("data", "name")
-
-Above, the ``name`` attribute will read/write the field ``"name"``
-from the JSON column ``data``, after initializing it to an
-empty dictionary::
-
- >>> person = Person(name="foobar")
- >>> person.name
- foobar
-
-The extension also triggers a change event when the attribute is modified,
-so that there's no need to use :class:`~.mutable.MutableDict` in order
-to track this change.
-
-.. seealso::
-
- :ref:`indexable_toplevel`
-
-.. _change_3250:
-
-New options allowing explicit persistence of NULL over a default
-----------------------------------------------------------------
-
-Related to the new JSON-NULL support added to PostgreSQL as part of
-:ref:`change_3514`, the base :class:`.TypeEngine` class now supports
-a method :meth:`.TypeEngine.evaluates_none` which allows a positive set
-of the ``None`` value on an attribute to be persisted as NULL, rather than
-omitting the column from the INSERT statement, which has the effect of using
-the column-level default. This allows a mapper-level
-configuration of the existing object-level technique of assigning
-:func:`_expression.null` to the attribute.
-
-.. seealso::
-
- :ref:`session_forcing_null`
-
-:ticket:`3250`
-
-
-.. _change_3582:
-
-Further Fixes to single-table inheritance querying
---------------------------------------------------
-
-Continuing from 1.0's :ref:`migration_3177`, the :class:`_query.Query` should
-no longer inappropriately add the "single inheritance" criteria when the
-query is against a subquery expression such as an exists::
-
- class Widget(Base):
- __tablename__ = "widget"
- id = Column(Integer, primary_key=True)
- type = Column(String)
- data = Column(String)
- __mapper_args__ = {"polymorphic_on": type}
-
-
- class FooWidget(Widget):
- __mapper_args__ = {"polymorphic_identity": "foo"}
-
-
- q = session.query(FooWidget).filter(FooWidget.data == "bar").exists()
-
- session.query(q).all()
-
-Produces:
-
-.. sourcecode:: sql
-
- SELECT EXISTS (SELECT 1
- FROM widget
- WHERE widget.data = :data_1 AND widget.type IN (:type_1)) AS anon_1
-
-The IN clause on the inside is appropriate, in order to limit to FooWidget
-objects, however previously the IN clause would also be generated a second
-time on the outside of the subquery.
-
-:ticket:`3582`
-
-.. _change_3680:
-
-Improved Session state when a SAVEPOINT is cancelled by the database
---------------------------------------------------------------------
-
-A common case with MySQL is that a SAVEPOINT is cancelled when a deadlock
-occurs within the transaction. The :class:`.Session` has been modified
-to deal with this failure mode slightly more gracefully, such that the
-outer, non-savepoint transaction still remains usable::
-
- s = Session()
- s.begin_nested()
-
- s.add(SomeObject())
-
- try:
- # assume the flush fails, flush goes to rollback to the
- # savepoint and that also fails
- s.flush()
- except Exception as err:
- print("Something broke, and our SAVEPOINT vanished too")
-
- # this is the SAVEPOINT transaction, marked as
- # DEACTIVE so the rollback() call succeeds
- s.rollback()
-
- # this is the outermost transaction, remains ACTIVE
- # so rollback() or commit() can succeed
- s.rollback()
-
-This issue is a continuation of :ticket:`2696` where we emit a warning
-so that the original error can be seen when running on Python 2, even though
-the SAVEPOINT exception takes precedence. On Python 3, exceptions are chained
-so both failures are reported individually.
-
-
-:ticket:`3680`
-
-.. _change_3677:
-
-Erroneous "new instance X conflicts with persistent instance Y" flush errors fixed
-----------------------------------------------------------------------------------
-
-The :meth:`.Session.rollback` method is responsible for removing objects
-that were INSERTed into the database, e.g. moved from pending to persistent,
-within that now rolled-back transaction. Objects that make this state
-change are tracked in a weak-referencing collection, and if an object is
-garbage collected from that collection, the :class:`.Session` no longer worries
-about it (it would otherwise not scale for operations that insert many new
-objects within a transaction). However, an issue arises if the application
-re-loads that same garbage-collected row within the transaction, before the
-rollback occurs; if a strong reference to this object remains into the next
-transaction, the fact that this object was not inserted and should be
-removed would be lost, and the flush would incorrectly raise an error::
-
- from sqlalchemy import Column, create_engine
- from sqlalchemy.orm import Session
- from sqlalchemy.ext.declarative import declarative_base
-
- Base = declarative_base()
-
-
- class A(Base):
- __tablename__ = "a"
- id = Column(Integer, primary_key=True)
-
-
- e = create_engine("sqlite://", echo=True)
- Base.metadata.create_all(e)
-
- s = Session(e)
-
- # persist an object
- s.add(A(id=1))
- s.flush()
-
- # rollback buffer loses reference to A
-
- # load it again, rollback buffer knows nothing
- # about it
- a1 = s.query(A).first()
-
- # roll back the transaction; all state is expired but the
- # "a1" reference remains
- s.rollback()
-
- # previous "a1" conflicts with the new one because we aren't
- # checking that it never got committed
- s.add(A(id=1))
- s.commit()
-
-The above program would raise:
-
-.. sourcecode:: text
-
- FlushError: New instance <User at 0x7f0287eca4d0> with identity key
- (<class 'test.orm.test_transaction.User'>, ('u1',)) conflicts
- with persistent instance <User at 0x7f02889c70d0>
-
-The bug is that when the above exception is raised, the unit of work
-is operating upon the original object assuming it's a live row, when in
-fact the object is expired and upon testing reveals that it's gone. The
-fix tests this condition now, so in the SQL log we see:
-
-.. sourcecode:: sql
-
- BEGIN (implicit)
-
- INSERT INTO a (id) VALUES (?)
- (1,)
-
- SELECT a.id AS a_id FROM a LIMIT ? OFFSET ?
- (1, 0)
-
- ROLLBACK
-
- BEGIN (implicit)
-
- SELECT a.id AS a_id FROM a WHERE a.id = ?
- (1,)
-
- INSERT INTO a (id) VALUES (?)
- (1,)
-
- COMMIT
-
-Above, the unit of work now does a SELECT for the row we're about to report
-as a conflict for, sees that it doesn't exist, and proceeds normally.
-The expense of this SELECT is only incurred in the case when we would have
-erroneously raised an exception in any case.
-
-
-:ticket:`3677`
-
-.. _change_2349:
-
-passive_deletes feature for joined-inheritance mappings
--------------------------------------------------------
-
-A joined-table inheritance mapping may now allow a DELETE to proceed
-as a result of :meth:`.Session.delete`, which only emits DELETE for the
-base table, and not the subclass table, allowing configured ON DELETE CASCADE
-to take place for the configured foreign keys. This is configured using
-the :paramref:`.orm.mapper.passive_deletes` option::
-
- from sqlalchemy import Column, Integer, String, ForeignKey, create_engine
- from sqlalchemy.orm import Session
- from sqlalchemy.ext.declarative import declarative_base
-
- Base = declarative_base()
-
-
- class A(Base):
- __tablename__ = "a"
- id = Column("id", Integer, primary_key=True)
- type = Column(String)
-
- __mapper_args__ = {
- "polymorphic_on": type,
- "polymorphic_identity": "a",
- "passive_deletes": True,
- }
-
-
- class B(A):
- __tablename__ = "b"
- b_table_id = Column("b_table_id", Integer, primary_key=True)
- bid = Column("bid", Integer, ForeignKey("a.id", ondelete="CASCADE"))
- data = Column("data", String)
-
- __mapper_args__ = {"polymorphic_identity": "b"}
-
-With the above mapping, the :paramref:`.orm.mapper.passive_deletes` option
-is configured on the base mapper; it takes effect for all non-base mappers
-that are descendants of the mapper with the option set. A DELETE for
-an object of type ``B`` no longer needs to retrieve the primary key value
-of ``b_table_id`` if unloaded, nor does it need to emit a DELETE statement
-for the table itself::
-
- session.delete(some_b)
- session.commit()
-
-Will emit SQL as:
-
-.. sourcecode:: sql
-
- DELETE FROM a WHERE a.id = %(id)s
- -- {'id': 1}
- COMMIT
-
-As always, the target database must have foreign key support with
-ON DELETE CASCADE enabled.
-
-:ticket:`2349`
-
-.. _change_3630:
-
-Same-named backrefs will not raise an error when applied to concrete inheritance subclasses
--------------------------------------------------------------------------------------------
-
-The following mapping has always been possible without issue::
-
- class A(Base):
- __tablename__ = "a"
- id = Column(Integer, primary_key=True)
- b = relationship("B", foreign_keys="B.a_id", backref="a")
-
-
- class A1(A):
- __tablename__ = "a1"
- id = Column(Integer, primary_key=True)
- b = relationship("B", foreign_keys="B.a1_id", backref="a1")
- __mapper_args__ = {"concrete": True}
-
-
- class B(Base):
- __tablename__ = "b"
- id = Column(Integer, primary_key=True)
-
- a_id = Column(ForeignKey("a.id"))
- a1_id = Column(ForeignKey("a1.id"))
-
-Above, even though class ``A`` and class ``A1`` have a relationship
-named ``b``, no conflict warning or error occurs because class ``A1`` is
-marked as "concrete".
-
-However, if the relationships were configured the other way, an error
-would occur::
-
- class A(Base):
- __tablename__ = "a"
- id = Column(Integer, primary_key=True)
-
-
- class A1(A):
- __tablename__ = "a1"
- id = Column(Integer, primary_key=True)
- __mapper_args__ = {"concrete": True}
-
-
- class B(Base):
- __tablename__ = "b"
- id = Column(Integer, primary_key=True)
-
- a_id = Column(ForeignKey("a.id"))
- a1_id = Column(ForeignKey("a1.id"))
-
- a = relationship("A", backref="b")
- a1 = relationship("A1", backref="b")
-
-The fix enhances the backref feature so that an error is not emitted,
-as well as an additional check within the mapper logic to bypass warning
-for an attribute being replaced.
-
-:ticket:`3630`
-
-.. _change_3749:
-
-Same-named relationships on inheriting mappers no longer warn
--------------------------------------------------------------
-
-When creating two mappers in an inheritance scenario, placing a relationship
-on both with the same name would emit the warning
-"relationship '<name>' on mapper <name> supersedes the same relationship
-on inherited mapper '<name>'; this can cause dependency issues during flush".
-An example is as follows::
-
- class A(Base):
- __tablename__ = "a"
- id = Column(Integer, primary_key=True)
- bs = relationship("B")
-
-
- class ASub(A):
- __tablename__ = "a_sub"
- id = Column(Integer, ForeignKey("a.id"), primary_key=True)
- bs = relationship("B")
-
-
- class B(Base):
- __tablename__ = "b"
- id = Column(Integer, primary_key=True)
- a_id = Column(ForeignKey("a.id"))
-
-This warning dates back to the 0.4 series in 2007 and is based on a version of
-the unit of work code that has since been entirely rewritten. Currently, there
-is no known issue with the same-named relationships being placed on a base
-class and a descendant class, so the warning is lifted. However, note that
-this use case is likely not prevalent in real world use due to the warning.
-While rudimentary test support is added for this use case, it is possible that
-some new issue with this pattern may be identified.
-
-.. versionadded:: 1.1.0b3
-
-:ticket:`3749`
-
-.. _change_3653:
-
-Hybrid properties and methods now propagate the docstring as well as .info
---------------------------------------------------------------------------
-
-A hybrid method or property will now reflect the ``__doc__`` value
-present in the original docstring::
-
- class A(Base):
- __tablename__ = "a"
- id = Column(Integer, primary_key=True)
-
- name = Column(String)
-
- @hybrid_property
- def some_name(self):
- """The name field"""
- return self.name
-
-The above value of ``A.some_name.__doc__`` is now honored::
-
- >>> A.some_name.__doc__
- The name field
-
-However, to accomplish this, the mechanics of hybrid properties necessarily
-becomes more complex. Previously, the class-level accessor for a hybrid
-would be a simple pass-through, that is, this test would succeed::
-
- >>> assert A.name is A.some_name
-
-With the change, the expression returned by ``A.some_name`` is wrapped inside
-of its own ``QueryableAttribute`` wrapper::
-
- >>> A.some_name
- <sqlalchemy.orm.attributes.hybrid_propertyProxy object at 0x7fde03888230>
-
-A lot of testing went into making sure this wrapper works correctly, including
-for elaborate schemes like that of the
-`Custom Value Object <https://techspot.zzzeek.org/2011/10/21/hybrids-and-value-agnostic-types/>`_
-recipe, however we'll be looking to see that no other regressions occur for
-users.
-
-As part of this change, the :attr:`.hybrid_property.info` collection is now
-also propagated from the hybrid descriptor itself, rather than from the underlying
-expression. That is, accessing ``A.some_name.info`` now returns the same
-dictionary that you'd get from ``inspect(A).all_orm_descriptors['some_name'].info``::
-
- >>> A.some_name.info["foo"] = "bar"
- >>> from sqlalchemy import inspect
- >>> inspect(A).all_orm_descriptors["some_name"].info
- {'foo': 'bar'}
-
-Note that this ``.info`` dictionary is **separate** from that of a mapped attribute
-which the hybrid descriptor may be proxying directly; this is a behavioral
-change from 1.0. The wrapper will still proxy other useful attributes
-of a mirrored attribute such as :attr:`.QueryableAttribute.property` and
-:attr:`.QueryableAttribute.class_`.
-
-:ticket:`3653`
-
-.. _change_3601:
-
-Session.merge resolves pending conflicts the same as persistent
----------------------------------------------------------------
-
-The :meth:`.Session.merge` method will now track the identities of objects given
-within a graph to maintain primary key uniqueness before emitting an INSERT.
-When duplicate objects of the same identity are encountered, non-primary-key
-attributes are **overwritten** as the objects are encountered, which is
-essentially non-deterministic. This behavior matches that of how persistent
-objects, that is objects that are already located in the database via
-primary key, are already treated, so this behavior is more internally
-consistent.
-
-Given::
-
- u1 = User(id=7, name="x")
- u1.orders = [
- Order(description="o1", address=Address(id=1, email_address="a")),
- Order(description="o2", address=Address(id=1, email_address="b")),
- Order(description="o3", address=Address(id=1, email_address="c")),
- ]
-
- sess = Session()
- sess.merge(u1)
-
-Above, we merge a ``User`` object with three new ``Order`` objects, each referring to
-a distinct ``Address`` object, however each is given the same primary key.
-The current behavior of :meth:`.Session.merge` is to look in the identity
-map for this ``Address`` object, and use that as the target. If the object
-is present, meaning that the database already has a row for ``Address`` with
-primary key "1", we can see that the ``email_address`` field of the ``Address``
-will be overwritten three times, in this case with the values a, b and finally
-c.
-
-However, if the ``Address`` row for primary key "1" were not present, :meth:`.Session.merge`
-would instead create three separate ``Address`` instances, and we'd then get
-a primary key conflict upon INSERT. The new behavior is that the proposed
-primary key for these ``Address`` objects are tracked in a separate dictionary
-so that we merge the state of the three proposed ``Address`` objects onto
-one ``Address`` object to be inserted.
-
-It may have been preferable if the original case emitted some kind of warning
-that conflicting data were present in a single merge-tree, however the
-non-deterministic merging of values has been the behavior for many
-years for the persistent case; it now matches for the pending case. A
-feature that warns for conflicting values could still be feasible for both
-cases but would add considerable performance overhead as each column value
-would have to be compared during the merge.
-
-
-:ticket:`3601`
-
-.. _change_3708:
-
-Fix involving many-to-one object moves with user-initiated foreign key manipulations
-------------------------------------------------------------------------------------
-
-A bug has been fixed involving the mechanics of replacing a many-to-one
-reference to an object with another object. During the attribute operation,
-the location of the object that was previously referred to now makes use of the
-database-committed foreign key value, rather than the current foreign key
-value. The main effect of the fix is that a backref event towards a collection
-will fire off more accurately when a many-to-one change is made, even if the
-foreign key attribute was manually moved to the new value beforehand. Assume a
-mapping of the classes ``Parent`` and ``SomeClass``, where ``SomeClass.parent``
-refers to ``Parent`` and ``Parent.items`` refers to the collection of
-``SomeClass`` objects::
-
- some_object = SomeClass()
- session.add(some_object)
- some_object.parent_id = some_parent.id
- some_object.parent = some_parent
-
-Above, we've made a pending object ``some_object``, manipulated its foreign key
-towards ``Parent`` to refer to it, *then* we actually set up the relationship.
-Before the bug fix, the backref would not have fired off::
-
- # before the fix
- assert some_object not in some_parent.items
-
-The fix now is that when we seek to locate the previous value of
-``some_object.parent``, we disregard the parent id that's been manually set,
-and we look for the database-committed value. In this case, it's None because
-the object is pending, so the event system logs ``some_object.parent``
-as a net change::
-
- # after the fix, backref fired off for some_object.parent = some_parent
- assert some_object in some_parent.items
-
-While it is discouraged to manipulate foreign key attributes that are managed
-by relationships, there is limited support for this use case. Applications
-that manipulate foreign keys in order to allow loads to proceed will often make
-use of the :meth:`.Session.enable_relationship_loading` and
-:attr:`.RelationshipProperty.load_on_pending` features, which cause
-relationships to emit lazy loads based on in-memory foreign key values that
-aren't persisted. Whether or not these features are in use, this behavioral
-improvement will now be apparent.
-
-:ticket:`3708`
-
-.. _change_3662:
-
-Improvements to the Query.correlate method with polymorphic entities
---------------------------------------------------------------------
-
-In recent SQLAlchemy versions, the SQL generated by many forms of
-"polymorphic" queries has a more "flat" form than it used to, where
-a JOIN of several tables is no longer bundled into a subquery unconditionally.
-To accommodate this, the :meth:`_query.Query.correlate` method now extracts the
-individual tables from such a polymorphic selectable and ensures that all
-are part of the "correlate" for the subquery. Assuming the
-``Person/Manager/Engineer->Company`` setup from the mapping documentation,
-using with_polymorphic::
-
- sess.query(Person.name).filter(
- sess.query(Company.name)
- .filter(Company.company_id == Person.company_id)
- .correlate(Person)
- .as_scalar()
- == "Elbonia, Inc."
- )
-
-The above query now produces:
-
-.. sourcecode:: sql
-
- SELECT people.name AS people_name
- FROM people
- LEFT OUTER JOIN engineers ON people.person_id = engineers.person_id
- LEFT OUTER JOIN managers ON people.person_id = managers.person_id
- WHERE (SELECT companies.name
- FROM companies
- WHERE companies.company_id = people.company_id) = ?
-
-Before the fix, the call to ``correlate(Person)`` would inadvertently
-attempt to correlate to the join of ``Person``, ``Engineer`` and ``Manager``
-as a single unit, so ``Person`` wouldn't be correlated:
-
-.. sourcecode:: sql
-
- -- old, incorrect query
- SELECT people.name AS people_name
- FROM people
- LEFT OUTER JOIN engineers ON people.person_id = engineers.person_id
- LEFT OUTER JOIN managers ON people.person_id = managers.person_id
- WHERE (SELECT companies.name
- FROM companies, people
- WHERE companies.company_id = people.company_id) = ?
-
-Using correlated subqueries against polymorphic mappings still has some
-unpolished edges. If for example ``Person`` is polymorphically linked
-to a so-called "concrete polymorphic union" query, the above subquery
-may not correctly refer to this subquery. In all cases, a way to refer
-to the "polymorphic" entity fully is to create an :func:`.aliased` object
-from it first::
-
- # works with all SQLAlchemy versions and all types of polymorphic
- # aliasing.
-
- paliased = aliased(Person)
- sess.query(paliased.name).filter(
- sess.query(Company.name)
- .filter(Company.company_id == paliased.company_id)
- .correlate(paliased)
- .as_scalar()
- == "Elbonia, Inc."
- )
-
-The :func:`.aliased` construct guarantees that the "polymorphic selectable"
-is wrapped in a subquery. By referring to it explicitly in the correlated
-subquery, the polymorphic form is correctly used.
-
-:ticket:`3662`
-
-.. _change_3081:
-
-Stringify of Query will consult the Session for the correct dialect
--------------------------------------------------------------------
-
-Calling ``str()`` on a :class:`_query.Query` object will consult the :class:`.Session`
-for the correct "bind" to use, in order to render the SQL that would be
-passed to the database. In particular this allows a :class:`_query.Query` that
-refers to dialect-specific SQL constructs to be renderable, assuming the
-:class:`_query.Query` is associated with an appropriate :class:`.Session`.
-Previously, this behavior would only take effect if the :class:`_schema.MetaData`
-to which the mappings were associated were itself bound to the target
-:class:`_engine.Engine`.
-
-If neither the underlying :class:`_schema.MetaData` nor the :class:`.Session` are
-associated with any bound :class:`_engine.Engine`, then the fallback to the
-"default" dialect is used to generate the SQL string.
-
-.. seealso::
-
- :ref:`change_3631`
-
-:ticket:`3081`
-
-.. _change_3431:
-
-Joined eager loading where the same entity is present multiple times in one row
--------------------------------------------------------------------------------
-
-A fix has been made to the case has been made whereby an attribute will be
-loaded via joined eager loading, even if the entity was already loaded from the
-row on a different "path" that doesn't include the attribute. This is a
-deep use case that's hard to reproduce, but the general idea is as follows::
-
- class A(Base):
- __tablename__ = "a"
- id = Column(Integer, primary_key=True)
- b_id = Column(ForeignKey("b.id"))
- c_id = Column(ForeignKey("c.id"))
-
- b = relationship("B")
- c = relationship("C")
-
-
- class B(Base):
- __tablename__ = "b"
- id = Column(Integer, primary_key=True)
- c_id = Column(ForeignKey("c.id"))
-
- c = relationship("C")
-
-
- class C(Base):
- __tablename__ = "c"
- id = Column(Integer, primary_key=True)
- d_id = Column(ForeignKey("d.id"))
- d = relationship("D")
-
-
- class D(Base):
- __tablename__ = "d"
- id = Column(Integer, primary_key=True)
-
-
- c_alias_1 = aliased(C)
- c_alias_2 = aliased(C)
-
- q = s.query(A)
- q = q.join(A.b).join(c_alias_1, B.c).join(c_alias_1.d)
- q = q.options(
- contains_eager(A.b).contains_eager(B.c, alias=c_alias_1).contains_eager(C.d)
- )
- q = q.join(c_alias_2, A.c)
- q = q.options(contains_eager(A.c, alias=c_alias_2))
-
-The above query emits SQL like this:
-
-.. sourcecode:: sql
-
- SELECT
- d.id AS d_id,
- c_1.id AS c_1_id, c_1.d_id AS c_1_d_id,
- b.id AS b_id, b.c_id AS b_c_id,
- c_2.id AS c_2_id, c_2.d_id AS c_2_d_id,
- a.id AS a_id, a.b_id AS a_b_id, a.c_id AS a_c_id
- FROM
- a
- JOIN b ON b.id = a.b_id
- JOIN c AS c_1 ON c_1.id = b.c_id
- JOIN d ON d.id = c_1.d_id
- JOIN c AS c_2 ON c_2.id = a.c_id
-
-We can see that the ``c`` table is selected from twice; once in the context
-of ``A.b.c -> c_alias_1`` and another in the context of ``A.c -> c_alias_2``.
-Also, we can see that it is quite possible that the ``C`` identity for a
-single row is the **same** for both ``c_alias_1`` and ``c_alias_2``, meaning
-two sets of columns in one row result in only one new object being added
-to the identity map.
-
-The query options above only call for the attribute ``C.d`` to be loaded
-in the context of ``c_alias_1``, and not ``c_alias_2``. So whether or not
-the final ``C`` object we get in the identity map has the ``C.d`` attribute
-loaded depends on how the mappings are traversed, which while not completely
-random, is essentially non-deterministic. The fix is that even if the
-loader for ``c_alias_1`` is processed after that of ``c_alias_2`` for a
-single row where they both refer to the same identity, the ``C.d``
-element will still be loaded. Previously, the loader did not seek to
-modify the load of an entity that was already loaded via a different path.
-The loader that reaches the entity first has always been non-deterministic,
-so this fix may be detectable as a behavioral change in some situations and
-not others.
-
-The fix includes tests for two variants of the "multiple paths to one entity"
-case, and the fix should hopefully cover all other scenarios of this nature.
-
-:ticket:`3431`
-
-
-New MutableList and MutableSet helpers added to the mutation tracking extension
--------------------------------------------------------------------------------
-
-New helper classes :class:`.MutableList` and :class:`.MutableSet` have been
-added to the :ref:`mutable_toplevel` extension, to complement the existing
-:class:`.MutableDict` helper.
-
-:ticket:`3297`
-
-.. _change_3512:
-
-New "raise" / "raise_on_sql" loader strategies
-----------------------------------------------
-
-To assist with the use case of preventing unwanted lazy loads from occurring
-after a series of objects are loaded, the new "lazy='raise'" and
-"lazy='raise_on_sql'" strategies and
-corresponding loader option :func:`_orm.raiseload` may be applied to a
-relationship attribute which will cause it to raise ``InvalidRequestError``
-when a non-eagerly-loaded attribute is accessed for read. The two variants
-test for either a lazy load of any variety, including those that would
-only return None or retrieve from the identity map::
-
- >>> from sqlalchemy.orm import raiseload
- >>> a1 = s.query(A).options(raiseload(A.some_b)).first()
- >>> a1.some_b
- Traceback (most recent call last):
- ...
- sqlalchemy.exc.InvalidRequestError: 'A.some_b' is not available due to lazy='raise'
-
-Or a lazy load only where SQL would be emitted::
-
- >>> from sqlalchemy.orm import raiseload
- >>> a1 = s.query(A).options(raiseload(A.some_b, sql_only=True)).first()
- >>> a1.some_b
- Traceback (most recent call last):
- ...
- sqlalchemy.exc.InvalidRequestError: 'A.bs' is not available due to lazy='raise_on_sql'
-
-:ticket:`3512`
-
-.. _change_3394:
-
-Mapper.order_by is deprecated
------------------------------
-
-This old parameter from the very first versions of SQLAlchemy was part of
-the original design of the ORM which featured the :class:`_orm.Mapper` object
-as a public-facing query structure. This role has long since been replaced
-by the :class:`_query.Query` object, where we use :meth:`_query.Query.order_by` to
-indicate the ordering of results in a way that works consistently for any
-combination of SELECT statements, entities and SQL expressions. There are
-many areas in which :paramref:`_orm.Mapper.order_by` doesn't work as expected
-(or what would be expected is not clear), such as when queries are combined
-into unions; these cases are not supported.
-
-
-:ticket:`3394`
-
-New Features and Improvements - Core
-====================================
-
-.. _change_3803:
-
-Engines now invalidate connections, run error handlers for BaseException
-------------------------------------------------------------------------
-
-.. versionadded:: 1.1 this change is a late add to the 1.1 series just
- prior to 1.1 final, and is not present in the 1.1 beta releases.
-
-The Python ``BaseException`` class is below that of ``Exception`` but is the
-identifiable base for system-level exceptions such as ``KeyboardInterrupt``,
-``SystemExit``, and notably the ``GreenletExit`` exception that's used by
-eventlet and gevent. This exception class is now intercepted by the exception-
-handling routines of :class:`_engine.Connection`, and includes handling by the
-:meth:`_events.ConnectionEvents.handle_error` event. The :class:`_engine.Connection` is now
-**invalidated** by default in the case of a system level exception that is not
-a subclass of ``Exception``, as it is assumed an operation was interrupted and
-the connection may be in an unusable state. The MySQL drivers are most
-targeted by this change however the change is across all DBAPIs.
-
-Note that upon invalidation, the immediate DBAPI connection used by
-:class:`_engine.Connection` is disposed, and the :class:`_engine.Connection`, if still
-being used subsequent to the exception raise, will use a new
-DBAPI connection for subsequent operations upon next use; however, the state of
-any transaction in progress is lost and the appropriate ``.rollback()`` method
-must be called if applicable before this re-use can proceed.
-
-In order to identify this change, it was straightforward to demonstrate a pymysql or
-mysqlclient / MySQL-Python connection moving into a corrupted state when
-these exceptions occur in the middle of the connection doing its work;
-the connection would then be returned to the connection pool where subsequent
-uses would fail, or even before returning to the pool would cause secondary
-failures in context managers that call ``.rollback()`` upon the exception
-catch. The behavior here is expected to reduce
-the incidence of the MySQL error "commands out of sync", as well as the
-``ResourceClosedError`` which can occur when the MySQL driver fails to
-report ``cursor.description`` correctly, when running under greenlet
-conditions where greenlets are killed, or where ``KeyboardInterrupt`` exceptions
-are handled without exiting the program entirely.
-
-The behavior is distinct from the usual auto-invalidation feature, in that it
-does not assume that the backend database itself has been shut down or
-restarted; it does not recycle the entire connection pool as is the case
-for usual DBAPI disconnect exceptions.
-
-This change should be a net improvement for all users with the exception
-of **any application that currently intercepts ``KeyboardInterrupt`` or
-``GreenletExit`` and wishes to continue working within the same transaction**.
-Such an operation is theoretically possible with other DBAPIs that do not appear to be
-impacted by ``KeyboardInterrupt`` such as psycopg2. For these DBAPIs,
-the following workaround will disable the connection from being recycled
-for specific exceptions::
-
-
- engine = create_engine("postgresql+psycopg2://")
-
-
- @event.listens_for(engine, "handle_error")
- def cancel_disconnect(ctx):
- if isinstance(ctx.original_exception, KeyboardInterrupt):
- ctx.is_disconnect = False
-
-:ticket:`3803`
-
-
-.. _change_2551:
-
-CTE Support for INSERT, UPDATE, DELETE
---------------------------------------
-
-One of the most widely requested features is support for common table
-expressions (CTE) that work with INSERT, UPDATE, DELETE, and is now implemented.
-An INSERT/UPDATE/DELETE can both draw from a WITH clause that's stated at the
-top of the SQL, as well as can be used as a CTE itself in the context of
-a larger statement.
-
-As part of this change, an INSERT from SELECT that includes a CTE will now
-render the CTE at the top of the entire statement, rather than nested
-in the SELECT statement as was the case in 1.0.
-
-Below is an example that renders UPDATE, INSERT and SELECT all in one
-statement:
-
-.. sourcecode:: pycon+sql
-
- >>> from sqlalchemy import table, column, select, literal, exists
- >>> orders = table(
- ... "orders",
- ... column("region"),
- ... column("amount"),
- ... column("product"),
- ... column("quantity"),
- ... )
- >>>
- >>> upsert = (
- ... orders.update()
- ... .where(orders.c.region == "Region1")
- ... .values(amount=1.0, product="Product1", quantity=1)
- ... .returning(*(orders.c._all_columns))
- ... .cte("upsert")
- ... )
- >>>
- >>> insert = orders.insert().from_select(
- ... orders.c.keys(),
- ... select([literal("Region1"), literal(1.0), literal("Product1"), literal(1)]).where(
- ... ~exists(upsert.select())
- ... ),
- ... )
- >>>
- >>> print(insert) # Note: formatting added for clarity
- {printsql}WITH upsert AS
- (UPDATE orders SET amount=:amount, product=:product, quantity=:quantity
- WHERE orders.region = :region_1
- RETURNING orders.region, orders.amount, orders.product, orders.quantity
- )
- INSERT INTO orders (region, amount, product, quantity)
- SELECT
- :param_1 AS anon_1, :param_2 AS anon_2,
- :param_3 AS anon_3, :param_4 AS anon_4
- WHERE NOT (
- EXISTS (
- SELECT upsert.region, upsert.amount,
- upsert.product, upsert.quantity
- FROM upsert))
-
-:ticket:`2551`
-
-.. _change_3049:
-
-Support for RANGE and ROWS specification within window functions
-----------------------------------------------------------------
-
-New :paramref:`.expression.over.range_` and :paramref:`.expression.over.rows` parameters allow
-RANGE and ROWS expressions for window functions:
-
-.. sourcecode:: pycon+sql
-
- >>> from sqlalchemy import func
-
- >>> print(func.row_number().over(order_by="x", range_=(-5, 10)))
- {printsql}row_number() OVER (ORDER BY x RANGE BETWEEN :param_1 PRECEDING AND :param_2 FOLLOWING){stop}
-
- >>> print(func.row_number().over(order_by="x", rows=(None, 0)))
- {printsql}row_number() OVER (ORDER BY x ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW){stop}
-
- >>> print(func.row_number().over(order_by="x", range_=(-2, None)))
- {printsql}row_number() OVER (ORDER BY x RANGE BETWEEN :param_1 PRECEDING AND UNBOUNDED FOLLOWING){stop}
-
-:paramref:`.expression.over.range_` and :paramref:`.expression.over.rows` are specified as
-2-tuples and indicate negative and positive values for specific ranges,
-0 for "CURRENT ROW", and None for UNBOUNDED.
-
-.. seealso::
-
- :ref:`tutorial_window_functions`
-
-:ticket:`3049`
-
-.. _change_2857:
-
-Support for the SQL LATERAL keyword
------------------------------------
-
-The LATERAL keyword is currently known to only be supported by PostgreSQL 9.3
-and greater, however as it is part of the SQL standard support for this keyword
-is added to Core. The implementation of :meth:`_expression.Select.lateral` employs
-special logic beyond just rendering the LATERAL keyword to allow for
-correlation of tables that are derived from the same FROM clause as the
-selectable, e.g. lateral correlation:
-
-.. sourcecode:: pycon+sql
-
- >>> from sqlalchemy import table, column, select, true
- >>> people = table("people", column("people_id"), column("age"), column("name"))
- >>> books = table("books", column("book_id"), column("owner_id"))
- >>> subq = (
- ... select([books.c.book_id])
- ... .where(books.c.owner_id == people.c.people_id)
- ... .lateral("book_subq")
- ... )
- >>> print(select([people]).select_from(people.join(subq, true())))
- {printsql}SELECT people.people_id, people.age, people.name
- FROM people JOIN LATERAL (SELECT books.book_id AS book_id
- FROM books WHERE books.owner_id = people.people_id)
- AS book_subq ON true
-
-.. seealso::
-
- :ref:`tutorial_lateral_correlation`
-
- :class:`_expression.Lateral`
-
- :meth:`_expression.Select.lateral`
-
-
-:ticket:`2857`
-
-.. _change_3718:
-
-Support for TABLESAMPLE
------------------------
-
-The SQL standard TABLESAMPLE can be rendered using the
-:meth:`_expression.FromClause.tablesample` method, which returns a :class:`_expression.TableSample`
-construct similar to an alias::
-
- from sqlalchemy import func
-
- selectable = people.tablesample(func.bernoulli(1), name="alias", seed=func.random())
- stmt = select([selectable.c.people_id])
-
-Assuming ``people`` with a column ``people_id``, the above
-statement would render as:
-
-.. sourcecode:: sql
-
- SELECT alias.people_id FROM
- people AS alias TABLESAMPLE bernoulli(:bernoulli_1)
- REPEATABLE (random())
-
-:ticket:`3718`
-
-.. _change_3216:
-
-The ``.autoincrement`` directive is no longer implicitly enabled for a composite primary key column
----------------------------------------------------------------------------------------------------
-
-SQLAlchemy has always had the convenience feature of enabling the backend database's
-"autoincrement" feature for a single-column integer primary key; by "autoincrement"
-we mean that the database column will include whatever DDL directives the
-database provides in order to indicate an auto-incrementing integer identifier,
-such as the SERIAL keyword on PostgreSQL or AUTO_INCREMENT on MySQL, and additionally
-that the dialect will receive these generated values from the execution
-of a :meth:`_schema.Table.insert` construct using techniques appropriate to that
-backend.
-
-What's changed is that this feature no longer turns on automatically for a
-*composite* primary key; previously, a table definition such as::
-
- Table(
- "some_table",
- metadata,
- Column("x", Integer, primary_key=True),
- Column("y", Integer, primary_key=True),
- )
-
-Would have "autoincrement" semantics applied to the ``'x'`` column, only
-because it's first in the list of primary key columns. In order to
-disable this, one would have to turn off ``autoincrement`` on all columns::
-
- # old way
- Table(
- "some_table",
- metadata,
- Column("x", Integer, primary_key=True, autoincrement=False),
- Column("y", Integer, primary_key=True, autoincrement=False),
- )
-
-With the new behavior, the composite primary key will not have autoincrement
-semantics unless a column is marked explicitly with ``autoincrement=True``::
-
- # column 'y' will be SERIAL/AUTO_INCREMENT/ auto-generating
- Table(
- "some_table",
- metadata,
- Column("x", Integer, primary_key=True),
- Column("y", Integer, primary_key=True, autoincrement=True),
- )
-
-In order to anticipate some potential backwards-incompatible scenarios,
-the :meth:`_schema.Table.insert` construct will perform more thorough checks
-for missing primary key values on composite primary key columns that don't
-have autoincrement set up; given a table such as::
-
- Table(
- "b",
- metadata,
- Column("x", Integer, primary_key=True),
- Column("y", Integer, primary_key=True),
- )
-
-An INSERT emitted with no values for this table will produce this warning:
-
-.. sourcecode:: text
-
- SAWarning: Column 'b.x' is marked as a member of the primary
- key for table 'b', but has no Python-side or server-side default
- generator indicated, nor does it indicate 'autoincrement=True',
- and no explicit value is passed. Primary key columns may not
- store NULL. Note that as of SQLAlchemy 1.1, 'autoincrement=True'
- must be indicated explicitly for composite (e.g. multicolumn)
- primary keys if AUTO_INCREMENT/SERIAL/IDENTITY behavior is
- expected for one of the columns in the primary key. CREATE TABLE
- statements are impacted by this change as well on most backends.
-
-For a column that is receiving primary key values from a server-side
-default or something less common such as a trigger, the presence of a
-value generator can be indicated using :class:`.FetchedValue`::
-
- Table(
- "b",
- metadata,
- Column("x", Integer, primary_key=True, server_default=FetchedValue()),
- Column("y", Integer, primary_key=True, server_default=FetchedValue()),
- )
-
-For the very unlikely case where a composite primary key is actually intended
-to store NULL in one or more of its columns (only supported on SQLite and MySQL),
-specify the column with ``nullable=True``::
-
- Table(
- "b",
- metadata,
- Column("x", Integer, primary_key=True),
- Column("y", Integer, primary_key=True, nullable=True),
- )
-
-In a related change, the ``autoincrement`` flag may be set to True
-on a column that has a client-side or server-side default. This typically
-will not have much impact on the behavior of the column during an INSERT.
-
-
-.. seealso::
-
- :ref:`change_mysql_3216`
-
-:ticket:`3216`
-
-.. _change_is_distinct_from:
-
-Support for IS DISTINCT FROM and IS NOT DISTINCT FROM
------------------------------------------------------
-
-New operators :meth:`.ColumnOperators.is_distinct_from` and
-:meth:`.ColumnOperators.isnot_distinct_from` allow the IS DISTINCT
-FROM and IS NOT DISTINCT FROM sql operation:
-
-.. sourcecode:: pycon+sql
-
- >>> print(column("x").is_distinct_from(None))
- {printsql}x IS DISTINCT FROM NULL{stop}
-
-Handling is provided for NULL, True and False:
-
-.. sourcecode:: pycon+sql
-
- >>> print(column("x").isnot_distinct_from(False))
- {printsql}x IS NOT DISTINCT FROM false{stop}
-
-For SQLite, which doesn't have this operator, "IS" / "IS NOT" is rendered,
-which on SQLite works for NULL unlike other backends:
-
-.. sourcecode:: pycon+sql
-
- >>> from sqlalchemy.dialects import sqlite
- >>> print(column("x").is_distinct_from(None).compile(dialect=sqlite.dialect()))
- {printsql}x IS NOT NULL{stop}
-
-.. _change_1957:
-
-Core and ORM support for FULL OUTER JOIN
-----------------------------------------
-
-The new flag :paramref:`.FromClause.outerjoin.full`, available at the Core
-and ORM level, instructs the compiler to render ``FULL OUTER JOIN``
-where it would normally render ``LEFT OUTER JOIN``::
-
- stmt = select([t1]).select_from(t1.outerjoin(t2, full=True))
-
-The flag also works at the ORM level::
-
- q = session.query(MyClass).outerjoin(MyOtherClass, full=True)
-
-:ticket:`1957`
-
-.. _change_3501:
-
-ResultSet column matching enhancements; positional column setup for textual SQL
--------------------------------------------------------------------------------
-
-A series of improvements were made to the :class:`_engine.ResultProxy` system
-in the 1.0 series as part of :ticket:`918`, which reorganizes the internals
-to match cursor-bound result columns with table/ORM metadata positionally,
-rather than by matching names, for compiled SQL constructs that contain full
-information about the result rows to be returned. This allows a dramatic savings
-on Python overhead as well as much greater accuracy in linking ORM and Core
-SQL expressions to result rows. In 1.1, this reorganization has been taken
-further internally, and also has been made available to pure-text SQL
-constructs via the use of the recently added :meth:`_expression.TextClause.columns` method.
-
-TextAsFrom.columns() now works positionally
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The :meth:`_expression.TextClause.columns` method, added in 0.9, accepts column-based arguments
-positionally; in 1.1, when all columns are passed positionally, the correlation
-of these columns to the ultimate result set is also performed positionally.
-The key advantage here is that textual SQL can now be linked to an ORM-
-level result set without the need to deal with ambiguous or duplicate column
-names, or with having to match labeling schemes to ORM-level labeling schemes. All
-that's needed now is the same ordering of columns within the textual SQL
-and the column arguments passed to :meth:`_expression.TextClause.columns`::
-
-
- from sqlalchemy import text
-
- stmt = text(
- "SELECT users.id, addresses.id, users.id, "
- "users.name, addresses.email_address AS email "
- "FROM users JOIN addresses ON users.id=addresses.user_id "
- "WHERE users.id = 1"
- ).columns(User.id, Address.id, Address.user_id, User.name, Address.email_address)
-
- query = session.query(User).from_statement(stmt).options(contains_eager(User.addresses))
- result = query.all()
-
-Above, the textual SQL contains the column "id" three times, which would
-normally be ambiguous. Using the new feature, we can apply the mapped
-columns from the ``User`` and ``Address`` class directly, even linking
-the ``Address.user_id`` column to the ``users.id`` column in textual SQL
-for fun, and the :class:`_query.Query` object will receive rows that are correctly
-targetable as needed, including for an eager load.
-
-This change is **backwards incompatible** with code that passes the columns
-to the method with a different ordering than is present in the textual statement.
-It is hoped that this impact will be low due to the fact that this
-method has always been documented illustrating the columns being passed in the same order as that of the
-textual SQL statement, as would seem intuitive, even though the internals
-weren't checking for this. The method itself was only added as of 0.9 in
-any case and may not yet have widespread use. Notes on exactly how to handle
-this behavioral change for applications using it are at :ref:`behavior_change_3501`.
-
-.. seealso::
-
- :ref:`tutorial_select_arbitrary_text`
-
- :ref:`behavior_change_3501` - backwards compatibility remarks
-
-Positional matching is trusted over name-based matching for Core/ORM SQL constructs
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Another aspect of this change is that the rules for matching columns have also been modified
-to rely upon "positional" matching more fully for compiled SQL constructs
-as well. Given a statement like the following::
-
- ua = users.alias("ua")
- stmt = select([users.c.user_id, ua.c.user_id])
-
-The above statement will compile to:
-
-.. sourcecode:: sql
-
- SELECT users.user_id, ua.user_id FROM users, users AS ua
-
-In 1.0, the above statement when executed would be matched to its original
-compiled construct using positional matching, however because the statement
-contains the ``'user_id'`` label duplicated, the "ambiguous column" rule
-would still get involved and prevent the columns from being fetched from a row.
-As of 1.1, the "ambiguous column" rule does not affect an exact match from
-a column construct to the SQL column, which is what the ORM uses to
-fetch columns::
-
- result = conn.execute(stmt)
- row = result.first()
-
- # these both match positionally, so no error
- user_id = row[users.c.user_id]
- ua_id = row[ua.c.user_id]
-
- # this still raises, however
- user_id = row["user_id"]
-
-Much less likely to get an "ambiguous column" error message
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-As part of this change, the wording of the error message ``Ambiguous column
-name '<name>' in result set! try 'use_labels' option on select statement.``
-has been dialed back; as this message should now be extremely rare when using
-the ORM or Core compiled SQL constructs, it merely states
-``Ambiguous column name '<name>' in result set column descriptions``, and
-only when a result column is retrieved using the string name that is actually
-ambiguous, e.g. ``row['user_id']`` in the above example. It also now refers
-to the actual ambiguous name from the rendered SQL statement itself,
-rather than indicating the key or name that was local to the construct being
-used for the fetch.
-
-:ticket:`3501`
-
-.. _change_3292:
-
-Support for Python's native ``enum`` type and compatible forms
---------------------------------------------------------------
-
-The :class:`.Enum` type can now be constructed using any
-PEP-435 compliant enumerated type. When using this mode, input values
-and return values are the actual enumerated objects, not the
-string/integer/etc values::
-
- import enum
- from sqlalchemy import Table, MetaData, Column, Enum, create_engine
-
-
- class MyEnum(enum.Enum):
- one = 1
- two = 2
- three = 3
-
-
- t = Table("data", MetaData(), Column("value", Enum(MyEnum)))
-
- e = create_engine("sqlite://")
- t.create(e)
-
- e.execute(t.insert(), {"value": MyEnum.two})
- assert e.scalar(t.select()) is MyEnum.two
-
-The ``Enum.enums`` collection is now a list instead of a tuple
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-As part of the changes to :class:`.Enum`, the :attr:`.Enum.enums` collection
-of elements is now a list instead of a tuple. This because lists
-are appropriate for variable length sequences of homogeneous items where
-the position of the element is not semantically significant.
-
-:ticket:`3292`
-
-.. _change_gh_231:
-
-Negative integer indexes accommodated by Core result rows
----------------------------------------------------------
-
-The :class:`.RowProxy` object now accommodates single negative integer indexes
-like a regular Python sequence, both in the pure Python and C-extension
-version. Previously, negative values would only work in slices::
-
- >>> from sqlalchemy import create_engine
- >>> e = create_engine("sqlite://")
- >>> row = e.execute("select 1, 2, 3").first()
- >>> row[-1], row[-2], row[1], row[-2:2]
- 3 2 2 (2,)
-
-.. _change_3095:
-
-The ``Enum`` type now does in-Python validation of values
----------------------------------------------------------
-
-To accommodate for Python native enumerated objects, as well as for edge
-cases such as that of where a non-native ENUM type is used within an ARRAY
-and a CHECK constraint is infeasible, the :class:`.Enum` datatype now adds
-in-Python validation of input values when the :paramref:`.Enum.validate_strings`
-flag is used (1.1.0b2)::
-
-
- >>> from sqlalchemy import Table, MetaData, Column, Enum, create_engine
- >>> t = Table(
- ... "data",
- ... MetaData(),
- ... Column("value", Enum("one", "two", "three", validate_strings=True)),
- ... )
- >>> e = create_engine("sqlite://")
- >>> t.create(e)
- >>> e.execute(t.insert(), {"value": "four"})
- Traceback (most recent call last):
- ...
- sqlalchemy.exc.StatementError: (exceptions.LookupError)
- "four" is not among the defined enum values
- [SQL: u'INSERT INTO data (value) VALUES (?)']
- [parameters: [{'value': 'four'}]]
-
-This validation is turned off by default as there are already use cases
-identified where users don't want such validation (such as string comparisons).
-For non-string types, it necessarily takes place in all cases. The
-check also occurs unconditionally on the result-handling side as well, when
-values coming from the database are returned.
-
-This validation is in addition to the existing behavior of creating a
-CHECK constraint when a non-native enumerated type is used. The creation of
-this CHECK constraint can now be disabled using the new
-:paramref:`.Enum.create_constraint` flag.
-
-:ticket:`3095`
-
-.. _change_3730:
-
-Non-native boolean integer values coerced to zero/one/None in all cases
------------------------------------------------------------------------
-
-The :class:`.Boolean` datatype coerces Python booleans to integer values
-for backends that don't have a native boolean type, such as SQLite and
-MySQL. On these backends, a CHECK constraint is normally set up which
-ensures the values in the database are in fact one of these two values.
-However, MySQL ignores CHECK constraints, the constraint is optional, and
-an existing database might not have this constraint. The :class:`.Boolean`
-datatype has been repaired such that an incoming Python-side value that is
-already an integer value is coerced to zero or one, not just passed as-is;
-additionally, the C-extension version of the int-to-boolean processor for
-results now uses the same Python boolean interpretation of the value,
-rather than asserting an exact one or zero value. This is now consistent
-with the pure-Python int-to-boolean processor and is more forgiving of
-existing data already within the database. Values of None/NULL are as before
-retained as None/NULL.
-
-.. note::
-
- this change had an unintended side effect that the interpretation of non-
- integer values, such as strings, also changed in behavior such that the
- string value ``"0"`` would be interpreted as "true", but only on backends
- that don't have a native boolean datatype - on "native boolean" backends
- like PostgreSQL, the string value ``"0"`` is passed directly to the driver
- and is interpreted as "false". This is an inconsistency that did not occur
- with the previous implementation. It should be noted that passing strings or
- any other value outside of ``None``, ``True``, ``False``, ``1``, ``0`` to
- the :class:`.Boolean` datatype is **not supported** and version 1.2 will
- raise an error for this scenario (or possibly just emit a warning, TBD).
- See also :ticket:`4102`.
-
-
-:ticket:`3730`
-
-.. _change_2837:
-
-Large parameter and row values are now truncated in logging and exception displays
-----------------------------------------------------------------------------------
-
-A large value present as a bound parameter for a SQL statement, as well as a
-large value present in a result row, will now be truncated during display
-within logging, exception reporting, as well as ``repr()`` of the row itself::
-
- >>> from sqlalchemy import create_engine
- >>> import random
- >>> e = create_engine("sqlite://", echo="debug")
- >>> some_value = "".join(chr(random.randint(52, 85)) for i in range(5000))
- >>> row = e.execute("select ?", [some_value]).first()
- ... # (lines are wrapped for clarity) ...
- 2016-02-17 13:23:03,027 INFO sqlalchemy.engine.base.Engine select ?
- 2016-02-17 13:23:03,027 INFO sqlalchemy.engine.base.Engine
- ('E6@?>9HPOJB<<BHR:@=TS:5ILU=;JLM<4?B9<S48PTNG9>:=TSTLA;9K;9FPM4M8M@;NM6GU
- LUAEBT9QGHNHTHR5EP75@OER4?SKC;D:TFUMD:M>;C6U:JLM6R67GEK<A6@S@C@J7>4=4:P
- GJ7HQ6 ... (4702 characters truncated) ... J6IK546AJMB4N6S9L;;9AKI;=RJP
- HDSSOTNBUEEC9@Q:RCL:I@5?FO<9K>KJAGAO@E6@A7JI8O:J7B69T6<8;F:S;4BEIJS9HM
- K:;5OLPM@JR;R:J6<SOTTT=>Q>7T@I::OTDC:CC<=NGP6C>BC8N',)
- 2016-02-17 13:23:03,027 DEBUG sqlalchemy.engine.base.Engine Col ('?',)
- 2016-02-17 13:23:03,027 DEBUG sqlalchemy.engine.base.Engine
- Row (u'E6@?>9HPOJB<<BHR:@=TS:5ILU=;JLM<4?B9<S48PTNG9>:=TSTLA;9K;9FPM4M8M@;
- NM6GULUAEBT9QGHNHTHR5EP75@OER4?SKC;D:TFUMD:M>;C6U:JLM6R67GEK<A6@S@C@J7
- >4=4:PGJ7HQ ... (4703 characters truncated) ... J6IK546AJMB4N6S9L;;9AKI;=
- RJPHDSSOTNBUEEC9@Q:RCL:I@5?FO<9K>KJAGAO@E6@A7JI8O:J7B69T6<8;F:S;4BEIJS9HM
- K:;5OLPM@JR;R:J6<SOTTT=>Q>7T@I::OTDC:CC<=NGP6C>BC8N',)
- >>> print(row)
- (u'E6@?>9HPOJB<<BHR:@=TS:5ILU=;JLM<4?B9<S48PTNG9>:=TSTLA;9K;9FPM4M8M@;NM6
- GULUAEBT9QGHNHTHR5EP75@OER4?SKC;D:TFUMD:M>;C6U:JLM6R67GEK<A6@S@C@J7>4
- =4:PGJ7HQ ... (4703 characters truncated) ... J6IK546AJMB4N6S9L;;9AKI;
- =RJPHDSSOTNBUEEC9@Q:RCL:I@5?FO<9K>KJAGAO@E6@A7JI8O:J7B69T6<8;F:S;4BEIJS9H
- MK:;5OLPM@JR;R:J6<SOTTT=>Q>7T@I::OTDC:CC<=NGP6C>BC8N',)
-
-
-:ticket:`2837`
-
-
-.. _change_3619:
-
-JSON support added to Core
---------------------------
-
-As MySQL now has a JSON datatype in addition to the PostgreSQL JSON datatype,
-the core now gains a :class:`sqlalchemy.types.JSON` datatype that is the basis
-for both of these. Using this type allows access to the "getitem" operator
-as well as the "getpath" operator in a way that is agnostic across PostgreSQL
-and MySQL.
-
-The new datatype also has a series of improvements to the handling of
-NULL values as well as expression handling.
-
-.. seealso::
-
- :ref:`change_3547`
-
- :class:`_types.JSON`
-
- :class:`_postgresql.JSON`
-
- :class:`.mysql.JSON`
-
-:ticket:`3619`
-
-.. _change_3514:
-
-JSON "null" is inserted as expected with ORM operations, omitted when not present
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The :class:`_types.JSON` type and its descendant types :class:`_postgresql.JSON`
-and :class:`.mysql.JSON` have a flag :paramref:`.types.JSON.none_as_null` which
-when set to True indicates that the Python value ``None`` should translate
-into a SQL NULL rather than a JSON NULL value. This flag defaults to False,
-which means that the Python value ``None`` should result in a JSON NULL value.
-
-This logic would fail, and is now corrected, in the following circumstances:
-
-1. When the column also contained a default or server_default value,
-a positive value of ``None`` on the mapped attribute that expects to persist
-JSON "null" would still result in the column-level default being triggered,
-replacing the ``None`` value::
-
- class MyObject(Base):
- # ...
-
- json_value = Column(JSON(none_as_null=False), default="some default")
-
-
- # would insert "some default" instead of "'null'",
- # now will insert "'null'"
- obj = MyObject(json_value=None)
- session.add(obj)
- session.commit()
-
-2. When the column *did not* contain a default or server_default value, a missing
-value on a JSON column configured with none_as_null=False would still render
-JSON NULL rather than falling back to not inserting any value, behaving
-inconsistently vs. all other datatypes::
-
- class MyObject(Base):
- # ...
-
- some_other_value = Column(String(50))
- json_value = Column(JSON(none_as_null=False))
-
-
- # would result in NULL for some_other_value,
- # but json "'null'" for json_value. Now results in NULL for both
- # (the json_value is omitted from the INSERT)
- obj = MyObject()
- session.add(obj)
- session.commit()
-
-This is a behavioral change that is backwards incompatible for an application
-that was relying upon this to default a missing value as JSON null. This
-essentially establishes that a **missing value is distinguished from a present
-value of None**. See :ref:`behavior_change_3514` for further detail.
-
-3. When the :meth:`.Session.bulk_insert_mappings` method were used, ``None``
-would be ignored in all cases::
-
- # would insert SQL NULL and/or trigger defaults,
- # now inserts "'null'"
- session.bulk_insert_mappings(MyObject, [{"json_value": None}])
-
-The :class:`_types.JSON` type now implements the
-:attr:`.TypeEngine.should_evaluate_none` flag,
-indicating that ``None`` should not be ignored here; it is configured
-automatically based on the value of :paramref:`.types.JSON.none_as_null`.
-Thanks to :ticket:`3061`, we can differentiate when the value ``None`` is actively
-set by the user versus when it was never set at all.
-
-The feature applies as well to the new base :class:`_types.JSON` type
-and its descendant types.
-
-:ticket:`3514`
-
-.. _change_3514_jsonnull:
-
-New JSON.NULL Constant Added
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-To ensure that an application can always have full control at the value level
-of whether a :class:`_types.JSON`, :class:`_postgresql.JSON`, :class:`.mysql.JSON`,
-or :class:`_postgresql.JSONB` column
-should receive a SQL NULL or JSON ``"null"`` value, the constant
-:attr:`.types.JSON.NULL` has been added, which in conjunction with
-:func:`.null` can be used to determine fully between SQL NULL and
-JSON ``"null"``, regardless of what :paramref:`.types.JSON.none_as_null` is set
-to::
-
- from sqlalchemy import null
- from sqlalchemy.dialects.postgresql import JSON
-
- obj1 = MyObject(json_value=null()) # will *always* insert SQL NULL
- obj2 = MyObject(json_value=JSON.NULL) # will *always* insert JSON string "null"
-
- session.add_all([obj1, obj2])
- session.commit()
-
-The feature applies as well to the new base :class:`_types.JSON` type
-and its descendant types.
-
-:ticket:`3514`
-
-.. _change_3516:
-
-Array support added to Core; new ANY and ALL operators
-------------------------------------------------------
-
-Along with the enhancements made to the PostgreSQL :class:`_postgresql.ARRAY`
-type described in :ref:`change_3503`, the base class of :class:`_postgresql.ARRAY`
-itself has been moved to Core in a new class :class:`_types.ARRAY`.
-
-Arrays are part of the SQL standard, as are several array-oriented functions
-such as ``array_agg()`` and ``unnest()``. In support of these constructs
-for not just PostgreSQL but also potentially for other array-capable backends
-in the future such as DB2, the majority of array logic for SQL expressions
-is now in Core. The :class:`_types.ARRAY` type still **only works on
-PostgreSQL**, however it can be used directly, supporting special array
-use cases such as indexed access, as well as support for the ANY and ALL::
-
- mytable = Table("mytable", metadata, Column("data", ARRAY(Integer, dimensions=2)))
-
- expr = mytable.c.data[5][6]
-
- expr = mytable.c.data[5].any(12)
-
-In support of ANY and ALL, the :class:`_types.ARRAY` type retains the same
-:meth:`.types.ARRAY.Comparator.any` and :meth:`.types.ARRAY.Comparator.all` methods
-from the PostgreSQL type, but also exports these operations to new
-standalone operator functions :func:`_expression.any_` and
-:func:`_expression.all_`. These two functions work in more
-of the traditional SQL way, allowing a right-side expression form such
-as::
-
- from sqlalchemy import any_, all_
-
- select([mytable]).where(12 == any_(mytable.c.data[5]))
-
-For the PostgreSQL-specific operators "contains", "contained_by", and
-"overlaps", one should continue to use the :class:`_postgresql.ARRAY`
-type directly, which provides all functionality of the :class:`_types.ARRAY`
-type as well.
-
-The :func:`_expression.any_` and :func:`_expression.all_` operators
-are open-ended at the Core level, however their interpretation by backend
-databases is limited. On the PostgreSQL backend, the two operators
-**only accept array values**. Whereas on the MySQL backend, they
-**only accept subquery values**. On MySQL, one can use an expression
-such as::
-
- from sqlalchemy import any_, all_
-
- subq = select([mytable.c.value])
- select([mytable]).where(12 > any_(subq))
-
-:ticket:`3516`
-
-.. _change_3132:
-
-New Function features, "WITHIN GROUP", array_agg and set aggregate functions
-----------------------------------------------------------------------------
-
-With the new :class:`_types.ARRAY` type we can also implement a pre-typed
-function for the ``array_agg()`` SQL function that returns an array,
-which is now available using :class:`_functions.array_agg`::
-
- from sqlalchemy import func
-
- stmt = select([func.array_agg(table.c.value)])
-
-A PostgreSQL element for an aggregate ORDER BY is also added via
-:class:`_postgresql.aggregate_order_by`::
-
- from sqlalchemy.dialects.postgresql import aggregate_order_by
-
- expr = func.array_agg(aggregate_order_by(table.c.a, table.c.b.desc()))
- stmt = select([expr])
-
-Producing:
-
-.. sourcecode:: sql
-
- SELECT array_agg(table1.a ORDER BY table1.b DESC) AS array_agg_1 FROM table1
-
-The PG dialect itself also provides an :func:`_postgresql.array_agg` wrapper to
-ensure the :class:`_postgresql.ARRAY` type::
-
- from sqlalchemy.dialects.postgresql import array_agg
-
- stmt = select([array_agg(table.c.value).contains("foo")])
-
-Additionally, functions like ``percentile_cont()``, ``percentile_disc()``,
-``rank()``, ``dense_rank()`` and others that require an ordering via
-``WITHIN GROUP (ORDER BY <expr>)`` are now available via the
-:meth:`.FunctionElement.within_group` modifier::
-
- from sqlalchemy import func
-
- stmt = select(
- [
- department.c.id,
- func.percentile_cont(0.5).within_group(department.c.salary.desc()),
- ]
- )
-
-The above statement would produce SQL similar to:
-
-.. sourcecode:: sql
-
- SELECT department.id, percentile_cont(0.5)
- WITHIN GROUP (ORDER BY department.salary DESC)
-
-Placeholders with correct return types are now provided for these functions,
-and include :class:`.percentile_cont`, :class:`.percentile_disc`,
-:class:`.rank`, :class:`.dense_rank`, :class:`.mode`, :class:`.percent_rank`,
-and :class:`.cume_dist`.
-
-:ticket:`3132` :ticket:`1370`
-
-.. _change_2919:
-
-TypeDecorator now works with Enum, Boolean, "schema" types automatically
-------------------------------------------------------------------------
-
-The :class:`.SchemaType` types include types such as :class:`.Enum`
-and :class:`.Boolean` which, in addition to corresponding to a database
-type, also generate either a CHECK constraint or in the case of PostgreSQL
-ENUM a new CREATE TYPE statement, will now work automatically with
-:class:`.TypeDecorator` recipes. Previously, a :class:`.TypeDecorator` for
-an :class:`_postgresql.ENUM` had to look like this::
-
- # old way
- class MyEnum(TypeDecorator, SchemaType):
- impl = postgresql.ENUM("one", "two", "three", name="myenum")
-
- def _set_table(self, table):
- self.impl._set_table(table)
-
-The :class:`.TypeDecorator` now propagates those additional events so it
-can be done like any other type::
-
- # new way
- class MyEnum(TypeDecorator):
- impl = postgresql.ENUM("one", "two", "three", name="myenum")
-
-:ticket:`2919`
-
-.. _change_2685:
-
-Multi-Tenancy Schema Translation for Table objects
---------------------------------------------------
-
-To support the use case of an application that uses the same set of
-:class:`_schema.Table` objects in many schemas, such as schema-per-user, a new
-execution option :paramref:`.Connection.execution_options.schema_translate_map`
-is added. Using this mapping, a set of :class:`_schema.Table`
-objects can be made on a per-connection basis to refer to any set of schemas
-instead of the :paramref:`_schema.Table.schema` to which they were assigned. The
-translation works for DDL and SQL generation, as well as with the ORM.
-
-For example, if the ``User`` class were assigned the schema "per_user"::
-
- class User(Base):
- __tablename__ = "user"
- id = Column(Integer, primary_key=True)
-
- __table_args__ = {"schema": "per_user"}
-
-On each request, the :class:`.Session` can be set up to refer to a
-different schema each time::
-
- session = Session()
- session.connection(
- execution_options={"schema_translate_map": {"per_user": "account_one"}}
- )
-
- # will query from the ``account_one.user`` table
- session.query(User).get(5)
-
-.. seealso::
-
- :ref:`schema_translating`
-
-:ticket:`2685`
-
-.. _change_3631:
-
-"Friendly" stringification of Core SQL constructs without a dialect
--------------------------------------------------------------------
-
-Calling ``str()`` on a Core SQL construct will now produce a string
-in more cases than before, supporting various SQL constructs not normally
-present in default SQL such as RETURNING, array indexes, and non-standard
-datatypes:
-
-.. sourcecode:: pycon+sql
-
- >>> from sqlalchemy import table, column
- t>>> t = table('x', column('a'), column('b'))
- >>> print(t.insert().returning(t.c.a, t.c.b))
- {printsql}INSERT INTO x (a, b) VALUES (:a, :b) RETURNING x.a, x.b
-
-The ``str()`` function now calls upon an entirely separate dialect / compiler
-intended just for plain string printing without a specific dialect set up,
-so as more "just show me a string!" cases come up, these can be added
-to this dialect/compiler without impacting behaviors on real dialects.
-
-.. seealso::
-
- :ref:`change_3081`
-
-:ticket:`3631`
-
-.. _change_3531:
-
-The type_coerce function is now a persistent SQL element
---------------------------------------------------------
-
-The :func:`_expression.type_coerce` function previously would return
-an object either of type :class:`.BindParameter` or :class:`.Label`, depending
-on the input. An effect this would have was that in the case where expression
-transformations were used, such as the conversion of an element from a
-:class:`_schema.Column` to a :class:`.BindParameter` that's critical to ORM-level
-lazy loading, the type coercion information would not be used since it would
-have been lost already.
-
-To improve this behavior, the function now returns a persistent
-:class:`.TypeCoerce` container around the given expression, which itself
-remains unaffected; this construct is evaluated explicitly by the
-SQL compiler. This allows for the coercion of the inner expression
-to be maintained no matter how the statement is modified, including if
-the contained element is replaced with a different one, as is common
-within the ORM's lazy loading feature.
-
-The test case illustrating the effect makes use of a heterogeneous
-primaryjoin condition in conjunction with custom types and lazy loading.
-Given a custom type that applies a CAST as a "bind expression"::
-
- class StringAsInt(TypeDecorator):
- impl = String
-
- def column_expression(self, col):
- return cast(col, Integer)
-
- def bind_expression(self, value):
- return cast(value, String)
-
-Then, a mapping where we are equating a string "id" column on one
-table to an integer "id" column on the other::
-
- class Person(Base):
- __tablename__ = "person"
- id = Column(StringAsInt, primary_key=True)
-
- pets = relationship(
- "Pets",
- primaryjoin=(
- "foreign(Pets.person_id)" "==cast(type_coerce(Person.id, Integer), Integer)"
- ),
- )
-
-
- class Pets(Base):
- __tablename__ = "pets"
- id = Column("id", Integer, primary_key=True)
- person_id = Column("person_id", Integer)
-
-Above, in the :paramref:`_orm.relationship.primaryjoin` expression, we are
-using :func:`.type_coerce` to handle bound parameters passed via
-lazyloading as integers, since we already know these will come from
-our ``StringAsInt`` type which maintains the value as an integer in
-Python. We are then using :func:`.cast` so that as a SQL expression,
-the VARCHAR "id" column will be CAST to an integer for a regular non-
-converted join as with :meth:`_query.Query.join` or :func:`_orm.joinedload`.
-That is, a joinedload of ``.pets`` looks like:
-
-.. sourcecode:: sql
-
- SELECT person.id AS person_id, pets_1.id AS pets_1_id,
- pets_1.person_id AS pets_1_person_id
- FROM person
- LEFT OUTER JOIN pets AS pets_1
- ON pets_1.person_id = CAST(person.id AS INTEGER)
-
-Without the CAST in the ON clause of the join, strongly-typed databases
-such as PostgreSQL will refuse to implicitly compare the integer and fail.
-
-The lazyload case of ``.pets`` relies upon replacing
-the ``Person.id`` column at load time with a bound parameter, which receives
-a Python-loaded value. This replacement is specifically where the intent
-of our :func:`.type_coerce` function would be lost. Prior to the change,
-this lazy load comes out as:
-
-.. sourcecode:: sql
-
- SELECT pets.id AS pets_id, pets.person_id AS pets_person_id
- FROM pets
- WHERE pets.person_id = CAST(CAST(%(param_1)s AS VARCHAR) AS INTEGER)
- -- {'param_1': 5}
-
-Where above, we see that our in-Python value of ``5`` is CAST first
-to a VARCHAR, then back to an INTEGER in SQL; a double CAST which works,
-but is nevertheless not what we asked for.
-
-With the change, the :func:`.type_coerce` function maintains a wrapper
-even after the column is swapped out for a bound parameter, and the query now
-looks like:
-
-.. sourcecode:: sql
-
- SELECT pets.id AS pets_id, pets.person_id AS pets_person_id
- FROM pets
- WHERE pets.person_id = CAST(%(param_1)s AS INTEGER)
- -- {'param_1': 5}
-
-Where our outer CAST that's in our primaryjoin still takes effect, but the
-needless CAST that's in part of the ``StringAsInt`` custom type is removed
-as intended by the :func:`.type_coerce` function.
-
-
-:ticket:`3531`
-
-Key Behavioral Changes - ORM
-============================
-
-.. _behavior_change_3514:
-
-JSON Columns will not insert JSON NULL if no value is supplied and no default is established
---------------------------------------------------------------------------------------------
-
-As detailed in :ref:`change_3514`, :class:`_types.JSON` will not render
-a JSON "null" value if the value is missing entirely. To prevent SQL NULL,
-a default should be set up. Given the following mapping::
-
- class MyObject(Base):
- # ...
-
- json_value = Column(JSON(none_as_null=False), nullable=False)
-
-The following flush operation will fail with an integrity error::
-
- obj = MyObject() # note no json_value
- session.add(obj)
- session.commit() # will fail with integrity error
-
-If the default for the column should be JSON NULL, set this on the
-Column::
-
- class MyObject(Base):
- # ...
-
- json_value = Column(JSON(none_as_null=False), nullable=False, default=JSON.NULL)
-
-Or, ensure the value is present on the object::
-
- obj = MyObject(json_value=None)
- session.add(obj)
- session.commit() # will insert JSON NULL
-
-Note that setting ``None`` for the default is the same as omitting it entirely;
-the :paramref:`.types.JSON.none_as_null` flag does not impact the value of ``None``
-passed to :paramref:`_schema.Column.default` or :paramref:`_schema.Column.server_default`::
-
- # default=None is the same as omitting it entirely, does not apply JSON NULL
- json_value = Column(JSON(none_as_null=False), nullable=False, default=None)
-
-.. seealso::
-
- :ref:`change_3514`
-
-.. _change_3641:
-
-Columns no longer added redundantly with DISTINCT + ORDER BY
-------------------------------------------------------------
-
-A query such as the following will now augment only those columns
-that are missing from the SELECT list, without duplicates::
-
- q = (
- session.query(User.id, User.name.label("name"))
- .distinct()
- .order_by(User.id, User.name, User.fullname)
- )
-
-Produces:
-
-.. sourcecode:: sql
-
- SELECT DISTINCT user.id AS a_id, user.name AS name,
- user.fullname AS a_fullname
- FROM a ORDER BY user.id, user.name, user.fullname
-
-Previously, it would produce:
-
-.. sourcecode:: sql
-
- SELECT DISTINCT user.id AS a_id, user.name AS name, user.name AS a_name,
- user.fullname AS a_fullname
- FROM a ORDER BY user.id, user.name, user.fullname
-
-Where above, the ``user.name`` column is added unnecessarily. The results
-would not be affected, as the additional columns are not included in the
-result in any case, but the columns are unnecessary.
-
-Additionally, when the PostgreSQL DISTINCT ON format is used by passing
-expressions to :meth:`_query.Query.distinct`, the above "column adding" logic
-is disabled entirely.
-
-When the query is being bundled into a subquery for the purposes of
-joined eager loading, the "augment column list" rules are necessarily
-more aggressive so that the ORDER BY can still be satisfied, so this case
-remains unchanged.
-
-:ticket:`3641`
-
-.. _change_3776:
-
-Same-named @validates decorators will now raise an exception
-------------------------------------------------------------
-
-The :func:`_orm.validates` decorator is only intended to be created once
-per class for a particular attribute name. Creating more than one
-now raises an error, whereas previously it would silently pick only the
-last defined validator::
-
- class A(Base):
- __tablename__ = "a"
- id = Column(Integer, primary_key=True)
-
- data = Column(String)
-
- @validates("data")
- def _validate_data_one(self):
- assert "x" in data
-
- @validates("data")
- def _validate_data_two(self):
- assert "y" in data
-
-
- configure_mappers()
-
-Will raise:
-
-.. sourcecode:: text
-
- sqlalchemy.exc.InvalidRequestError: A validation function for mapped attribute 'data'
- on mapper Mapper|A|a already exists.
-
-:ticket:`3776`
-
-Key Behavioral Changes - Core
-=============================
-
-.. _behavior_change_3501:
-
-TextClause.columns() will match columns positionally, not by name, when passed positionally
--------------------------------------------------------------------------------------------
-
-The new behavior of the :meth:`_expression.TextClause.columns` method, which itself
-was recently added as of the 0.9 series, is that when
-columns are passed positionally without any additional keyword arguments,
-they are linked to the ultimate result set
-columns positionally, and no longer on name. It is hoped that the impact
-of this change will be low due to the fact that the method has always been documented
-illustrating the columns being passed in the same order as that of the
-textual SQL statement, as would seem intuitive, even though the internals
-weren't checking for this.
-
-An application that is using this method by passing :class:`_schema.Column` objects
-to it positionally must ensure that the position of those :class:`_schema.Column`
-objects matches the position in which these columns are stated in the
-textual SQL.
-
-E.g., code like the following::
-
- stmt = text("SELECT id, name, description FROM table")
-
- # no longer matches by name
- stmt = stmt.columns(my_table.c.name, my_table.c.description, my_table.c.id)
-
-Would no longer work as expected; the order of the columns given is now
-significant::
-
- # correct version
- stmt = stmt.columns(my_table.c.id, my_table.c.name, my_table.c.description)
-
-Possibly more likely, a statement that worked like this::
-
- stmt = text("SELECT * FROM table")
- stmt = stmt.columns(my_table.c.id, my_table.c.name, my_table.c.description)
-
-is now slightly risky, as the "*" specification will generally deliver columns
-in the order in which they are present in the table itself. If the structure
-of the table changes due to schema changes, this ordering may no longer be the same.
-Therefore when using :meth:`_expression.TextClause.columns`, it's advised to list out
-the desired columns explicitly in the textual SQL, though it's no longer
-necessary to worry about the names themselves in the textual SQL.
-
-.. seealso::
-
- :ref:`change_3501`
-
-.. _change_3809:
-
-String server_default now literal quoted
-----------------------------------------
-
-A server default passed to :paramref:`_schema.Column.server_default` as a plain
-Python string that has quotes embedded is now
-passed through the literal quoting system:
-
-.. sourcecode:: pycon+sql
-
- >>> from sqlalchemy.schema import MetaData, Table, Column, CreateTable
- >>> from sqlalchemy.types import String
- >>> t = Table("t", MetaData(), Column("x", String(), server_default="hi ' there"))
- >>> print(CreateTable(t))
- {printsql}CREATE TABLE t (
- x VARCHAR DEFAULT 'hi '' there'
- )
-
-Previously the quote would render directly. This change may be backwards
-incompatible for applications with such a use case who were working around
-the issue.
-
-
-:ticket:`3809`
-
-.. _change_2528:
-
-A UNION or similar of SELECTs with LIMIT/OFFSET/ORDER BY now parenthesizes the embedded selects
------------------------------------------------------------------------------------------------
-
-An issue that, like others, was long driven by SQLite's lack of capabilities
-has now been enhanced to work on all supporting backends. We refer to a query that
-is a UNION of SELECT statements that themselves contain row-limiting or ordering
-features which include LIMIT, OFFSET, and/or ORDER BY:
-
-.. sourcecode:: sql
-
- (SELECT x FROM table1 ORDER BY y LIMIT 1) UNION
- (SELECT x FROM table2 ORDER BY y LIMIT 2)
-
-The above query requires parenthesis within each sub-select in order to
-group the sub-results correctly. Production of the above statement in
-SQLAlchemy Core looks like::
-
- stmt1 = select([table1.c.x]).order_by(table1.c.y).limit(1)
- stmt2 = select([table1.c.x]).order_by(table2.c.y).limit(2)
-
- stmt = union(stmt1, stmt2)
-
-Previously, the above construct would not produce parenthesization for the
-inner SELECT statements, producing a query that fails on all backends.
-
-The above formats will **continue to fail on SQLite**; additionally, the format
-that includes ORDER BY but no LIMIT/SELECT will **continue to fail on Oracle**.
-This is not a backwards-incompatible change, because the queries fail without
-the parentheses as well; with the fix, the queries at least work on all other
-databases.
-
-In all cases, in order to produce a UNION of limited SELECT statements that
-also works on SQLite and in all cases on Oracle, the
-subqueries must be a SELECT of an ALIAS::
-
- stmt1 = select([table1.c.x]).order_by(table1.c.y).limit(1).alias().select()
- stmt2 = select([table2.c.x]).order_by(table2.c.y).limit(2).alias().select()
-
- stmt = union(stmt1, stmt2)
-
-This workaround works on all SQLAlchemy versions. In the ORM, it looks like::
-
- stmt1 = session.query(Model1).order_by(Model1.y).limit(1).subquery().select()
- stmt2 = session.query(Model2).order_by(Model2.y).limit(1).subquery().select()
-
- stmt = session.query(Model1).from_statement(stmt1.union(stmt2))
-
-The behavior here has many parallels to the "join rewriting" behavior
-introduced in SQLAlchemy 0.9 in :ref:`feature_joins_09`; however in this case
-we have opted not to add new rewriting behavior to accommodate this
-case for SQLite.
-The existing rewriting behavior is very complicated already, and the case of
-UNIONs with parenthesized SELECT statements is much less common than the
-"right-nested-join" use case of that feature.
-
-:ticket:`2528`
-
-
-Dialect Improvements and Changes - PostgreSQL
-=============================================
-
-.. _change_3529:
-
-Support for INSERT..ON CONFLICT (DO UPDATE | DO NOTHING)
---------------------------------------------------------
-
-The ``ON CONFLICT`` clause of ``INSERT`` added to PostgreSQL as of
-version 9.5 is now supported using a PostgreSQL-specific version of the
-:class:`_expression.Insert` object, via :func:`sqlalchemy.dialects.postgresql.dml.insert`.
-This :class:`_expression.Insert` subclass adds two new methods :meth:`_expression.Insert.on_conflict_do_update`
-and :meth:`_expression.Insert.on_conflict_do_nothing` which implement the full syntax
-supported by PostgreSQL 9.5 in this area::
-
- from sqlalchemy.dialects.postgresql import insert
-
- insert_stmt = insert(my_table).values(id="some_id", data="some data to insert")
-
- do_update_stmt = insert_stmt.on_conflict_do_update(
- index_elements=[my_table.c.id], set_=dict(data="some data to update")
- )
-
- conn.execute(do_update_stmt)
-
-The above will render:
-
-.. sourcecode:: sql
-
- INSERT INTO my_table (id, data)
- VALUES (:id, :data)
- ON CONFLICT id DO UPDATE SET data=:data_2
-
-.. seealso::
-
- :ref:`postgresql_insert_on_conflict`
-
-:ticket:`3529`
-
-.. _change_3499_postgresql:
-
-ARRAY and JSON types now correctly specify "unhashable"
--------------------------------------------------------
-
-As described in :ref:`change_3499`, the ORM relies upon being able to
-produce a hash function for column values when a query's selected entities
-mixes full ORM entities with column expressions. The ``hashable=False``
-flag is now correctly set on all of PG's "data structure" types, including
-:class:`_postgresql.ARRAY` and :class:`_postgresql.JSON`.
-The :class:`_postgresql.JSONB` and :class:`.HSTORE`
-types already included this flag. For :class:`_postgresql.ARRAY`,
-this is conditional based on the :paramref:`.postgresql.ARRAY.as_tuple`
-flag, however it should no longer be necessary to set this flag
-in order to have an array value present in a composed ORM row.
-
-.. seealso::
-
- :ref:`change_3499`
-
- :ref:`change_3503`
-
-:ticket:`3499`
-
-.. _change_3503:
-
-Correct SQL Types are Established from Indexed Access of ARRAY, JSON, HSTORE
-----------------------------------------------------------------------------
-
-For all three of :class:`_postgresql.ARRAY`, :class:`_postgresql.JSON` and :class:`.HSTORE`,
-the SQL type assigned to the expression returned by indexed access, e.g.
-``col[someindex]``, should be correct in all cases.
-
-This includes:
-
-* The SQL type assigned to indexed access of an :class:`_postgresql.ARRAY` takes into
- account the number of dimensions configured. An :class:`_postgresql.ARRAY` with three
- dimensions will return a SQL expression with a type of :class:`_postgresql.ARRAY` of
- one less dimension. Given a column with type ``ARRAY(Integer, dimensions=3)``,
- we can now perform this expression::
-
- int_expr = col[5][6][7] # returns an Integer expression object
-
- Previously, the indexed access to ``col[5]`` would return an expression of
- type :class:`.Integer` where we could no longer perform indexed access
- for the remaining dimensions, unless we used :func:`.cast` or :func:`.type_coerce`.
-
-* The :class:`_postgresql.JSON` and :class:`_postgresql.JSONB` types now mirror what PostgreSQL
- itself does for indexed access. This means that all indexed access for
- a :class:`_postgresql.JSON` or :class:`_postgresql.JSONB` type returns an expression that itself
- is *always* :class:`_postgresql.JSON` or :class:`_postgresql.JSONB` itself, unless the
- :attr:`~.postgresql.JSON.Comparator.astext` modifier is used. This means that whether
- the indexed access of the JSON structure ultimately refers to a string,
- list, number, or other JSON structure, PostgreSQL always considers it
- to be JSON itself unless it is explicitly cast differently. Like
- the :class:`_postgresql.ARRAY` type, this means that it is now straightforward
- to produce JSON expressions with multiple levels of indexed access::
-
- json_expr = json_col["key1"]["attr1"][5]
-
-* The "textual" type that is returned by indexed access of :class:`.HSTORE`
- as well as the "textual" type that is returned by indexed access of
- :class:`_postgresql.JSON` and :class:`_postgresql.JSONB` in conjunction with the
- :attr:`~.postgresql.JSON.Comparator.astext` modifier is now configurable; it defaults
- to :class:`_expression.TextClause` in both cases but can be set to a user-defined
- type using the :paramref:`.postgresql.JSON.astext_type` or
- :paramref:`.postgresql.HSTORE.text_type` parameters.
-
-.. seealso::
-
- :ref:`change_3503_cast`
-
-:ticket:`3499`
-:ticket:`3487`
-
-.. _change_3503_cast:
-
-The JSON cast() operation now requires ``.astext`` is called explicitly
------------------------------------------------------------------------
-
-As part of the changes in :ref:`change_3503`, the workings of the
-:meth:`_expression.ColumnElement.cast` operator on :class:`_postgresql.JSON` and
-:class:`_postgresql.JSONB` no longer implicitly invoke the
-:attr:`.postgresql.JSON.Comparator.astext` modifier; PostgreSQL's JSON/JSONB types
-support CAST operations to each other without the "astext" aspect.
-
-This means that in most cases, an application that was doing this::
-
- expr = json_col["somekey"].cast(Integer)
-
-Will now need to change to this::
-
- expr = json_col["somekey"].astext.cast(Integer)
-
-.. _change_2729:
-
-ARRAY with ENUM will now emit CREATE TYPE for the ENUM
-------------------------------------------------------
-
-A table definition like the following will now emit CREATE TYPE
-as expected::
-
- enum = Enum(
- "manager",
- "place_admin",
- "carwash_admin",
- "parking_admin",
- "service_admin",
- "tire_admin",
- "mechanic",
- "carwasher",
- "tire_mechanic",
- name="work_place_roles",
- )
-
-
- class WorkPlacement(Base):
- __tablename__ = "work_placement"
- id = Column(Integer, primary_key=True)
- roles = Column(ARRAY(enum))
-
-
- e = create_engine("postgresql://scott:tiger@localhost/test", echo=True)
- Base.metadata.create_all(e)
-
-emits:
-
-.. sourcecode:: sql
-
- CREATE TYPE work_place_roles AS ENUM (
- 'manager', 'place_admin', 'carwash_admin', 'parking_admin',
- 'service_admin', 'tire_admin', 'mechanic', 'carwasher',
- 'tire_mechanic')
-
- CREATE TABLE work_placement (
- id SERIAL NOT NULL,
- roles work_place_roles[],
- PRIMARY KEY (id)
- )
-
-
-:ticket:`2729`
-
-Check constraints now reflect
------------------------------
-
-The PostgreSQL dialect now supports reflection of CHECK constraints
-both within the method :meth:`_reflection.Inspector.get_check_constraints` as well
-as within :class:`_schema.Table` reflection within the :attr:`_schema.Table.constraints`
-collection.
-
-"Plain" and "Materialized" views can be inspected separately
-------------------------------------------------------------
-
-The new argument :paramref:`.PGInspector.get_view_names.include`
-allows specification of which sub-types of views should be returned::
-
- from sqlalchemy import inspect
-
- insp = inspect(engine)
-
- plain_views = insp.get_view_names(include="plain")
- all_views = insp.get_view_names(include=("plain", "materialized"))
-
-:ticket:`3588`
-
-
-Added tablespace option to Index
---------------------------------
-
-The :class:`.Index` object now accepts the argument ``postgresql_tablespace``
-in order to specify TABLESPACE, the same way as accepted by the
-:class:`_schema.Table` object.
-
-.. seealso::
-
- :ref:`postgresql_index_storage`
-
-:ticket:`3720`
-
-Support for PyGreSQL
---------------------
-
-The `PyGreSQL <https://pypi.org/project/PyGreSQL>`_ DBAPI is now supported.
-
-
-The "postgres" module is removed
---------------------------------
-
-The ``sqlalchemy.dialects.postgres`` module, long deprecated, is
-removed; this has emitted a warning for many years and projects
-should be calling upon ``sqlalchemy.dialects.postgresql``.
-Engine URLs of the form ``postgres://`` will still continue to function,
-however.
-
-Support for FOR UPDATE SKIP LOCKED / FOR NO KEY UPDATE / FOR KEY SHARE
------------------------------------------------------------------------
-
-The new parameters :paramref:`.GenerativeSelect.with_for_update.skip_locked`
-and :paramref:`.GenerativeSelect.with_for_update.key_share`
-in both Core and ORM apply a modification to a "SELECT...FOR UPDATE"
-or "SELECT...FOR SHARE" query on the PostgreSQL backend:
-
-* SELECT FOR NO KEY UPDATE::
-
- stmt = select([table]).with_for_update(key_share=True)
-
-* SELECT FOR UPDATE SKIP LOCKED::
-
- stmt = select([table]).with_for_update(skip_locked=True)
-
-* SELECT FOR KEY SHARE::
-
- stmt = select([table]).with_for_update(read=True, key_share=True)
-
-Dialect Improvements and Changes - MySQL
-========================================
-
-.. _change_3547:
-
-MySQL JSON Support
-------------------
-
-A new type :class:`.mysql.JSON` is added to the MySQL dialect supporting
-the JSON type newly added to MySQL 5.7. This type provides both persistence
-of JSON as well as rudimentary indexed-access using the ``JSON_EXTRACT``
-function internally. An indexable JSON column that works across MySQL
-and PostgreSQL can be achieved by using the :class:`_types.JSON` datatype
-common to both MySQL and PostgreSQL.
-
-.. seealso::
-
- :ref:`change_3619`
-
-:ticket:`3547`
-
-.. _change_3332:
-
-Added support for AUTOCOMMIT "isolation level"
-----------------------------------------------
-
-The MySQL dialect now accepts the value "AUTOCOMMIT" for the
-:paramref:`_sa.create_engine.isolation_level` and
-:paramref:`.Connection.execution_options.isolation_level`
-parameters::
-
- connection = engine.connect()
- connection = connection.execution_options(isolation_level="AUTOCOMMIT")
-
-The isolation level makes use of the various "autocommit" attributes
-provided by most MySQL DBAPIs.
-
-:ticket:`3332`
-
-.. _change_mysql_3216:
-
-No more generation of an implicit KEY for composite primary key w/ AUTO_INCREMENT
----------------------------------------------------------------------------------
-
-The MySQL dialect had the behavior such that if a composite primary key
-on an InnoDB table featured AUTO_INCREMENT on one of its columns which was
-not the first column, e.g.::
-
- t = Table(
- "some_table",
- metadata,
- Column("x", Integer, primary_key=True, autoincrement=False),
- Column("y", Integer, primary_key=True, autoincrement=True),
- mysql_engine="InnoDB",
- )
-
-DDL such as the following would be generated:
-
-.. sourcecode:: sql
-
- CREATE TABLE some_table (
- x INTEGER NOT NULL,
- y INTEGER NOT NULL AUTO_INCREMENT,
- PRIMARY KEY (x, y),
- KEY idx_autoinc_y (y)
- )ENGINE=InnoDB
-
-Note the above "KEY" with an auto-generated name; this is a change that
-found its way into the dialect many years ago in response to the issue that
-the AUTO_INCREMENT would otherwise fail on InnoDB without this additional KEY.
-
-This workaround has been removed and replaced with the much better system
-of just stating the AUTO_INCREMENT column *first* within the primary key:
-
-.. sourcecode:: sql
-
- CREATE TABLE some_table (
- x INTEGER NOT NULL,
- y INTEGER NOT NULL AUTO_INCREMENT,
- PRIMARY KEY (y, x)
- )ENGINE=InnoDB
-
-To maintain explicit control of the ordering of primary key columns,
-use the :class:`.PrimaryKeyConstraint` construct explicitly (1.1.0b2)
-(along with a KEY for the autoincrement column as required by MySQL), e.g.::
-
- t = Table(
- "some_table",
- metadata,
- Column("x", Integer, primary_key=True),
- Column("y", Integer, primary_key=True, autoincrement=True),
- PrimaryKeyConstraint("x", "y"),
- UniqueConstraint("y"),
- mysql_engine="InnoDB",
- )
-
-Along with the change :ref:`change_3216`, composite primary keys with
-or without auto increment are now easier to specify;
-:paramref:`_schema.Column.autoincrement`
-now defaults to the value ``"auto"`` and the ``autoincrement=False``
-directives are no longer needed::
-
- t = Table(
- "some_table",
- metadata,
- Column("x", Integer, primary_key=True),
- Column("y", Integer, primary_key=True, autoincrement=True),
- mysql_engine="InnoDB",
- )
-
-Dialect Improvements and Changes - SQLite
-=========================================
-
-.. _change_3634:
-
-Right-nested join workaround lifted for SQLite version 3.7.16
--------------------------------------------------------------
-
-In version 0.9, the feature introduced by :ref:`feature_joins_09` went
-through lots of effort to support rewriting of joins on SQLite to always
-use subqueries in order to achieve a "right-nested-join" effect, as
-SQLite has not supported this syntax for many years. Ironically,
-the version of SQLite noted in that migration note, 3.7.15.2, was the *last*
-version of SQLite to actually have this limitation! The next release was
-3.7.16 and support for right nested joins was quietly added. In 1.1, the work
-to identify the specific SQLite version and source commit where this change
-was made was done (SQLite's changelog refers to it with the cryptic phrase "Enhance
-the query optimizer to exploit transitive join constraints" without linking
-to any issue number, change number, or further explanation), and the workarounds
-present in this change are now lifted for SQLite when the DBAPI reports
-that version 3.7.16 or greater is in effect.
-
-:ticket:`3634`
-
-.. _change_3633:
-
-Dotted column names workaround lifted for SQLite version 3.10.0
----------------------------------------------------------------
-
-The SQLite dialect has long had a workaround for an issue where the database
-driver does not report the correct column names for some SQL result sets, in
-particular when UNION is used. The workaround is detailed at
-:ref:`sqlite_dotted_column_names`, and requires that SQLAlchemy assume that any
-column name with a dot in it is actually a ``tablename.columnname`` combination
-delivered via this buggy behavior, with an option to turn it off via the
-``sqlite_raw_colnames`` execution option.
-
-As of SQLite version 3.10.0, the bug in UNION and other queries has been fixed;
-like the change described in :ref:`change_3634`, SQLite's changelog only
-identifies it cryptically as "Added the colUsed field to sqlite3_index_info for
-use by the sqlite3_module.xBestIndex method", however SQLAlchemy's translation
-of these dotted column names is no longer required with this version, so is
-turned off when version 3.10.0 or greater is detected.
-
-Overall, the SQLAlchemy :class:`_engine.ResultProxy` as of the 1.0 series relies much
-less on column names in result sets when delivering results for Core and ORM
-SQL constructs, so the importance of this issue was already lessened in any
-case.
-
-:ticket:`3633`
-
-.. _change_sqlite_schemas:
-
-Improved Support for Remote Schemas
------------------------------------
-The SQLite dialect now implements :meth:`_reflection.Inspector.get_schema_names`
-and additionally has improved support for tables and indexes that are
-created and reflected from a remote schema, which in SQLite is a
-database that is assigned a name via the ``ATTACH`` statement; previously,
-the``CREATE INDEX`` DDL didn't work correctly for a schema-bound table
-and the :meth:`_reflection.Inspector.get_foreign_keys` method will now indicate the
-given schema in the results. Cross-schema foreign keys aren't supported.
-
-.. _change_3629:
-
-Reflection of the name of PRIMARY KEY constraints
--------------------------------------------------
-
-The SQLite backend now takes advantage of the "sqlite_master" view
-of SQLite in order to extract the name of the primary key constraint
-of a table from the original DDL, in the same way that is achieved for
-foreign key constraints in recent SQLAlchemy versions.
-
-:ticket:`3629`
-
-Check constraints now reflect
------------------------------
-
-The SQLite dialect now supports reflection of CHECK constraints
-both within the method :meth:`_reflection.Inspector.get_check_constraints` as well
-as within :class:`_schema.Table` reflection within the :attr:`_schema.Table.constraints`
-collection.
-
-ON DELETE and ON UPDATE foreign key phrases now reflect
--------------------------------------------------------
-
-The :class:`_reflection.Inspector` will now include ON DELETE and ON UPDATE
-phrases from foreign key constraints on the SQLite dialect, and the
-:class:`_schema.ForeignKeyConstraint` object as reflected as part of a
-:class:`_schema.Table` will also indicate these phrases.
-
-Dialect Improvements and Changes - SQL Server
-=============================================
-
-.. _change_3534:
-
-Added transaction isolation level support for SQL Server
---------------------------------------------------------
-
-All SQL Server dialects support transaction isolation level settings
-via the :paramref:`_sa.create_engine.isolation_level` and
-:paramref:`.Connection.execution_options.isolation_level`
-parameters. The four standard levels are supported as well as
-``SNAPSHOT``::
-
- engine = create_engine(
- "mssql+pyodbc://scott:tiger@ms_2008", isolation_level="REPEATABLE READ"
- )
-
-.. seealso::
-
- :ref:`mssql_isolation_level`
-
-:ticket:`3534`
-
-.. _change_3504:
-
-String / varlength types no longer represent "max" explicitly on reflection
----------------------------------------------------------------------------
-
-When reflecting a type such as :class:`.String`, :class:`_expression.TextClause`, etc.
-which includes a length, an "un-lengthed" type under SQL Server would
-copy the "length" parameter as the value ``"max"``::
-
- >>> from sqlalchemy import create_engine, inspect
- >>> engine = create_engine("mssql+pyodbc://scott:tiger@ms_2008", echo=True)
- >>> engine.execute("create table s (x varchar(max), y varbinary(max))")
- >>> insp = inspect(engine)
- >>> for col in insp.get_columns("s"):
- ... print(col["type"].__class__, col["type"].length)
- <class 'sqlalchemy.sql.sqltypes.VARCHAR'> max
- <class 'sqlalchemy.dialects.mssql.base.VARBINARY'> max
-
-The "length" parameter in the base types is expected to be an integer value
-or None only; None indicates unbounded length which the SQL Server dialect
-interprets as "max". The fix then is so that these lengths come
-out as None, so that the type objects work in non-SQL Server contexts::
-
- >>> for col in insp.get_columns("s"):
- ... print(col["type"].__class__, col["type"].length)
- <class 'sqlalchemy.sql.sqltypes.VARCHAR'> None
- <class 'sqlalchemy.dialects.mssql.base.VARBINARY'> None
-
-Applications which may have been relying on a direct comparison of the "length"
-value to the string "max" should consider the value of ``None`` to mean
-the same thing.
-
-:ticket:`3504`
-
-Support for "non clustered" on primary key to allow clustered elsewhere
------------------------------------------------------------------------
-
-The ``mssql_clustered`` flag available on :class:`.UniqueConstraint`,
-:class:`.PrimaryKeyConstraint`, :class:`.Index` now defaults to ``None``, and
-can be set to False which will render the NONCLUSTERED keyword in particular
-for a primary key, allowing a different index to be used as "clustered".
-
-.. seealso::
-
- :ref:`mssql_indexes`
-
-.. _change_3434:
-
-The legacy_schema_aliasing flag is now set to False
----------------------------------------------------
-
-SQLAlchemy 1.0.5 introduced the ``legacy_schema_aliasing`` flag to the
-MSSQL dialect, allowing so-called "legacy mode" aliasing to be turned off.
-This aliasing attempts to turn schema-qualified tables into aliases;
-given a table such as::
-
- account_table = Table(
- "account",
- metadata,
- Column("id", Integer, primary_key=True),
- Column("info", String(100)),
- schema="customer_schema",
- )
-
-The legacy mode of behavior will attempt to turn a schema-qualified table
-name into an alias:
-
-.. sourcecode:: pycon+sql
-
- >>> eng = create_engine("mssql+pymssql://mydsn", legacy_schema_aliasing=True)
- >>> print(account_table.select().compile(eng))
- {printsql}SELECT account_1.id, account_1.info
- FROM customer_schema.account AS account_1
-
-However, this aliasing has been shown to be unnecessary and in many cases
-produces incorrect SQL.
-
-In SQLAlchemy 1.1, the ``legacy_schema_aliasing`` flag now defaults to
-False, disabling this mode of behavior and allowing the MSSQL dialect to behave
-normally with schema-qualified tables. For applications which may rely
-on this behavior, set the flag back to True.
-
-
-:ticket:`3434`
-
-Dialect Improvements and Changes - Oracle
-=========================================
-
-Support for SKIP LOCKED
------------------------
-
-The new parameter :paramref:`.GenerativeSelect.with_for_update.skip_locked`
-in both Core and ORM will generate the "SKIP LOCKED" suffix for a
-"SELECT...FOR UPDATE" or "SELECT.. FOR SHARE" query.
+++ /dev/null
-=============================
-What's New in SQLAlchemy 1.2?
-=============================
-
-.. admonition:: About this Document
-
- This document describes changes between SQLAlchemy version 1.1
- and SQLAlchemy version 1.2.
-
-
-Introduction
-============
-
-This guide introduces what's new in SQLAlchemy version 1.2,
-and also documents changes which affect users migrating
-their applications from the 1.1 series of SQLAlchemy to 1.2.
-
-Please carefully review the sections on behavioral changes for
-potentially backwards-incompatible changes in behavior.
-
-Platform Support
-================
-
-Targeting Python 2.7 and Up
----------------------------
-
-SQLAlchemy 1.2 now moves the minimum Python version to 2.7, no longer
-supporting 2.6. New language features are expected to be merged
-into the 1.2 series that were not supported in Python 2.6. For Python 3 support,
-SQLAlchemy is currently tested on versions 3.5 and 3.6.
-
-
-New Features and Improvements - ORM
-===================================
-
-.. _change_3954:
-
-"Baked" loading now the default for lazy loads
-----------------------------------------------
-
-The :mod:`sqlalchemy.ext.baked` extension, first introduced in the 1.0 series,
-allows for the construction of a so-called :class:`.BakedQuery` object,
-which is an object that generates a :class:`_query.Query` object in conjunction
-with a cache key representing the structure of the query; this cache key
-is then linked to the resulting string SQL statement so that subsequent use
-of another :class:`.BakedQuery` with the same structure will bypass all the
-overhead of building the :class:`_query.Query` object, building the core
-:func:`_expression.select` object within, as well as the compilation of the :func:`_expression.select`
-into a string, cutting out well the majority of function call overhead normally
-associated with constructing and emitting an ORM :class:`_query.Query` object.
-
-The :class:`.BakedQuery` is now used by default by the ORM when it generates
-a "lazy" query for the lazy load of a :func:`_orm.relationship` construct, e.g.
-that of the default ``lazy="select"`` relationship loader strategy. This
-will allow for a significant reduction in function calls within the scope
-of an application's use of lazy load queries to load collections and related
-objects. Previously, this feature was available
-in 1.0 and 1.1 through the use of a global API method or by using the
-``baked_select`` strategy, it's now the only implementation for this behavior.
-The feature has also been improved such that the caching can still take place
-for objects that have additional loader options in effect subsequent
-to the lazy load.
-
-The caching behavior can be disabled on a per-relationship basis using the
-:paramref:`_orm.relationship.bake_queries` flag, which is available for
-very unusual cases, such as a relationship that uses a custom
-:class:`_query.Query` implementation that's not compatible with caching.
-
-
-:ticket:`3954`
-
-.. _change_3944:
-
-New "selectin" eager loading, loads all collections at once using IN
---------------------------------------------------------------------
-
-A new eager loader called "selectin" loading is added, which in many ways
-is similar to "subquery" loading, however produces a simpler SQL statement
-that is cacheable as well as more efficient.
-
-Given a query as below::
-
- q = (
- session.query(User)
- .filter(User.name.like("%ed%"))
- .options(subqueryload(User.addresses))
- )
-
-The SQL produced would be the query against ``User`` followed by the
-subqueryload for ``User.addresses`` (note the parameters are also listed):
-
-.. sourcecode:: sql
-
- SELECT users.id AS users_id, users.name AS users_name
- FROM users
- WHERE users.name LIKE ?
- ('%ed%',)
-
- SELECT addresses.id AS addresses_id,
- addresses.user_id AS addresses_user_id,
- addresses.email_address AS addresses_email_address,
- anon_1.users_id AS anon_1_users_id
- FROM (SELECT users.id AS users_id
- FROM users
- WHERE users.name LIKE ?) AS anon_1
- JOIN addresses ON anon_1.users_id = addresses.user_id
- ORDER BY anon_1.users_id
- ('%ed%',)
-
-With "selectin" loading, we instead get a SELECT that refers to the
-actual primary key values loaded in the parent query::
-
- q = (
- session.query(User)
- .filter(User.name.like("%ed%"))
- .options(selectinload(User.addresses))
- )
-
-Produces:
-
-.. sourcecode:: sql
-
- SELECT users.id AS users_id, users.name AS users_name
- FROM users
- WHERE users.name LIKE ?
- ('%ed%',)
-
- SELECT users_1.id AS users_1_id,
- addresses.id AS addresses_id,
- addresses.user_id AS addresses_user_id,
- addresses.email_address AS addresses_email_address
- FROM users AS users_1
- JOIN addresses ON users_1.id = addresses.user_id
- WHERE users_1.id IN (?, ?)
- ORDER BY users_1.id
- (1, 3)
-
-The above SELECT statement includes these advantages:
-
-* It doesn't use a subquery, just an INNER JOIN, meaning it will perform
- much better on a database like MySQL that doesn't like subqueries
-
-* Its structure is independent of the original query; in conjunction with the
- new :ref:`expanding IN parameter system <change_3953>` we can in most cases
- use the "baked" query to cache the string SQL, reducing per-query overhead
- significantly
-
-* Because the query only fetches for a given list of primary key identifiers,
- "selectin" loading is potentially compatible with :meth:`_query.Query.yield_per` to
- operate on chunks of a SELECT result at a time, provided that the
- database driver allows for multiple, simultaneous cursors (SQLite, PostgreSQL;
- **not** MySQL drivers or SQL Server ODBC drivers). Neither joined eager
- loading nor subquery eager loading are compatible with :meth:`_query.Query.yield_per`.
-
-The disadvantages of selectin eager loading are potentially large SQL
-queries, with large lists of IN parameters. The list of IN parameters themselves
-are chunked in groups of 500, so a result set of more than 500 lead objects
-will have more additional "SELECT IN" queries following. Also, support
-for composite primary keys depends on the database's ability to use
-tuples with IN, e.g.
-``(table.column_one, table_column_two) IN ((?, ?), (?, ?) (?, ?))``.
-Currently, PostgreSQL and MySQL are known to be compatible with this syntax,
-SQLite is not.
-
-.. seealso::
-
- :ref:`selectin_eager_loading`
-
-:ticket:`3944`
-
-.. _change_3948:
-
-"selectin" polymorphic loading, loads subclasses using separate IN queries
---------------------------------------------------------------------------
-
-Along similar lines as the "selectin" relationship loading feature just
-described at :ref:`change_3944` is "selectin" polymorphic loading. This
-is a polymorphic loading feature tailored primarily towards joined eager
-loading that allows the loading of the base entity to proceed with a simple
-SELECT statement, but then the attributes of the additional subclasses
-are loaded with additional SELECT statements:
-
-.. sourcecode:: pycon+sql
-
- >>> from sqlalchemy.orm import selectin_polymorphic
-
- >>> query = session.query(Employee).options(
- ... selectin_polymorphic(Employee, [Manager, Engineer])
- ... )
-
- >>> query.all()
- {execsql}SELECT
- employee.id AS employee_id,
- employee.name AS employee_name,
- employee.type AS employee_type
- FROM employee
- ()
-
- SELECT
- engineer.id AS engineer_id,
- employee.id AS employee_id,
- employee.type AS employee_type,
- engineer.engineer_name AS engineer_engineer_name
- FROM employee JOIN engineer ON employee.id = engineer.id
- WHERE employee.id IN (?, ?) ORDER BY employee.id
- (1, 2)
-
- SELECT
- manager.id AS manager_id,
- employee.id AS employee_id,
- employee.type AS employee_type,
- manager.manager_name AS manager_manager_name
- FROM employee JOIN manager ON employee.id = manager.id
- WHERE employee.id IN (?) ORDER BY employee.id
- (3,)
-
-.. seealso::
-
- :ref:`polymorphic_selectin`
-
-:ticket:`3948`
-
-.. _change_3058:
-
-ORM attributes that can receive ad-hoc SQL expressions
-------------------------------------------------------
-
-A new ORM attribute type :func:`_orm.query_expression` is added which
-is similar to :func:`_orm.deferred`, except its SQL expression
-is determined at query time using a new option :func:`_orm.with_expression`;
-if not specified, the attribute defaults to ``None``::
-
- from sqlalchemy.orm import query_expression
- from sqlalchemy.orm import with_expression
-
-
- class A(Base):
- __tablename__ = "a"
- id = Column(Integer, primary_key=True)
- x = Column(Integer)
- y = Column(Integer)
-
- # will be None normally...
- expr = query_expression()
-
-
- # but let's give it x + y
- a1 = session.query(A).options(with_expression(A.expr, A.x + A.y)).first()
- print(a1.expr)
-
-.. seealso::
-
- :ref:`mapper_querytime_expression`
-
-:ticket:`3058`
-
-.. _change_orm_959:
-
-ORM Support of multiple-table deletes
--------------------------------------
-
-The ORM :meth:`_query.Query.delete` method supports multiple-table criteria
-for DELETE, as introduced in :ref:`change_959`. The feature works
-in the same manner as multiple-table criteria for UPDATE, first
-introduced in 0.8 and described at :ref:`change_orm_2365`.
-
-Below, we emit a DELETE against ``SomeEntity``, adding
-a FROM clause (or equivalent, depending on backend)
-against ``SomeOtherEntity``::
-
- query(SomeEntity).filter(SomeEntity.id == SomeOtherEntity.id).filter(
- SomeOtherEntity.foo == "bar"
- ).delete()
-
-.. seealso::
-
- :ref:`change_959`
-
-:ticket:`959`
-
-.. _change_3229:
-
-Support for bulk updates of hybrids, composites
------------------------------------------------
-
-Both hybrid attributes (e.g. :mod:`sqlalchemy.ext.hybrid`) as well as composite
-attributes (:ref:`mapper_composite`) now support being used in the
-SET clause of an UPDATE statement when using :meth:`_query.Query.update`.
-
-For hybrids, simple expressions can be used directly, or the new decorator
-:meth:`.hybrid_property.update_expression` can be used to break a value
-into multiple columns/expressions::
-
- class Person(Base):
- # ...
-
- first_name = Column(String(10))
- last_name = Column(String(10))
-
- @hybrid.hybrid_property
- def name(self):
- return self.first_name + " " + self.last_name
-
- @name.expression
- def name(cls):
- return func.concat(cls.first_name, " ", cls.last_name)
-
- @name.update_expression
- def name(cls, value):
- f, l = value.split(" ", 1)
- return [(cls.first_name, f), (cls.last_name, l)]
-
-Above, an UPDATE can be rendered using::
-
- session.query(Person).filter(Person.id == 5).update({Person.name: "Dr. No"})
-
-Similar functionality is available for composites, where composite values
-will be broken out into their individual columns for bulk UPDATE::
-
- session.query(Vertex).update({Edge.start: Point(3, 4)})
-
-.. seealso::
-
- :ref:`hybrid_bulk_update`
-
-.. _change_3911_3912:
-
-Hybrid attributes support reuse among subclasses, redefinition of @getter
--------------------------------------------------------------------------
-
-The :class:`sqlalchemy.ext.hybrid.hybrid_property` class now supports
-calling mutators like ``@setter``, ``@expression`` etc. multiple times
-across subclasses, and now provides a ``@getter`` mutator, so that
-a particular hybrid can be repurposed across subclasses or other
-classes. This now is similar to the behavior of ``@property`` in standard
-Python::
-
- class FirstNameOnly(Base):
- # ...
-
- first_name = Column(String)
-
- @hybrid_property
- def name(self):
- return self.first_name
-
- @name.setter
- def name(self, value):
- self.first_name = value
-
-
- class FirstNameLastName(FirstNameOnly):
- # ...
-
- last_name = Column(String)
-
- @FirstNameOnly.name.getter
- def name(self):
- return self.first_name + " " + self.last_name
-
- @name.setter
- def name(self, value):
- self.first_name, self.last_name = value.split(" ", maxsplit=1)
-
- @name.expression
- def name(cls):
- return func.concat(cls.first_name, " ", cls.last_name)
-
-Above, the ``FirstNameOnly.name`` hybrid is referenced by the
-``FirstNameLastName`` subclass in order to repurpose it specifically to the
-new subclass. This is achieved by copying the hybrid object to a new one
-within each call to ``@getter``, ``@setter``, as well as in all other
-mutator methods like ``@expression``, leaving the previous hybrid's definition
-intact. Previously, methods like ``@setter`` would modify the existing
-hybrid in-place, interfering with the definition on the superclass.
-
-.. note:: Be sure to read the documentation at :ref:`hybrid_reuse_subclass`
- for important notes regarding how to override
- :meth:`.hybrid_property.expression`
- and :meth:`.hybrid_property.comparator`, as a special qualifier
- :attr:`.hybrid_property.overrides` may be necessary to avoid name
- conflicts with :class:`.QueryableAttribute` in some cases.
-
-.. note:: This change in ``@hybrid_property`` implies that when adding setters and
- other state to a ``@hybrid_property``, the **methods must retain the name
- of the original hybrid**, else the new hybrid with the additional state will
- be present on the class as the non-matching name. This is the same behavior
- as that of the ``@property`` construct that is part of standard Python::
-
- class FirstNameOnly(Base):
- @hybrid_property
- def name(self):
- return self.first_name
-
- # WRONG - will raise AttributeError: can't set attribute when
- # assigning to .name
- @name.setter
- def _set_name(self, value):
- self.first_name = value
-
-
- class FirstNameOnly(Base):
- @hybrid_property
- def name(self):
- return self.first_name
-
- # CORRECT - note regular Python @property works the same way
- @name.setter
- def name(self, value):
- self.first_name = value
-
-:ticket:`3911`
-
-:ticket:`3912`
-
-.. _change_3896_event:
-
-New bulk_replace event
-----------------------
-
-To suit the validation use case described in :ref:`change_3896_validates`,
-a new :meth:`.AttributeEvents.bulk_replace` method is added, which is
-called in conjunction with the :meth:`.AttributeEvents.append` and
-:meth:`.AttributeEvents.remove` events. "bulk_replace" is called before
-"append" and "remove" so that the collection can be modified ahead of comparison
-to the existing collection. After that, individual items
-are appended to a new target collection, firing off the "append"
-event for items new to the collection, as was the previous behavior.
-Below illustrates both "bulk_replace" and
-"append" at the same time, including that "append" will receive an object
-already handled by "bulk_replace" if collection assignment is used.
-A new symbol :attr:`~.attributes.OP_BULK_REPLACE` may be used to determine
-if this "append" event is the second part of a bulk replace::
-
- from sqlalchemy.orm.attributes import OP_BULK_REPLACE
-
-
- @event.listens_for(SomeObject.collection, "bulk_replace")
- def process_collection(target, values, initiator):
- values[:] = [_make_value(value) for value in values]
-
-
- @event.listens_for(SomeObject.collection, "append", retval=True)
- def process_collection(target, value, initiator):
- # make sure bulk_replace didn't already do it
- if initiator is None or initiator.op is not OP_BULK_REPLACE:
- return _make_value(value)
- else:
- return value
-
-:ticket:`3896`
-
-.. _change_3303:
-
-New "modified" event handler for sqlalchemy.ext.mutable
--------------------------------------------------------
-
-A new event handler :meth:`.AttributeEvents.modified` is added, which is
-triggered corresponding to calls to the :func:`.attributes.flag_modified`
-method, which is normally called from the :mod:`sqlalchemy.ext.mutable`
-extension::
-
- from sqlalchemy.ext.declarative import declarative_base
- from sqlalchemy.ext.mutable import MutableDict
- from sqlalchemy import event
-
- Base = declarative_base()
-
-
- class MyDataClass(Base):
- __tablename__ = "my_data"
- id = Column(Integer, primary_key=True)
- data = Column(MutableDict.as_mutable(JSONEncodedDict))
-
-
- @event.listens_for(MyDataClass.data, "modified")
- def modified_json(instance):
- print("json value modified:", instance.data)
-
-Above, the event handler will be triggered when an in-place change to the
-``.data`` dictionary occurs.
-
-:ticket:`3303`
-
-.. _change_3991:
-
-Added "for update" arguments to Session.refresh
-------------------------------------------------
-
-Added new argument :paramref:`.Session.refresh.with_for_update` to the
-:meth:`.Session.refresh` method. When the :meth:`_query.Query.with_lockmode`
-method were deprecated in favor of :meth:`_query.Query.with_for_update`,
-the :meth:`.Session.refresh` method was never updated to reflect
-the new option::
-
- session.refresh(some_object, with_for_update=True)
-
-The :paramref:`.Session.refresh.with_for_update` argument accepts a dictionary
-of options that will be passed as the same arguments which are sent to
-:meth:`_query.Query.with_for_update`::
-
- session.refresh(some_objects, with_for_update={"read": True})
-
-The new parameter supersedes the :paramref:`.Session.refresh.lockmode`
-parameter.
-
-:ticket:`3991`
-
-.. _change_3853:
-
-In-place mutation operators work for MutableSet, MutableList
-------------------------------------------------------------
-
-Implemented the in-place mutation operators ``__ior__``, ``__iand__``,
-``__ixor__`` and ``__isub__`` for :class:`.mutable.MutableSet` and ``__iadd__``
-for :class:`.mutable.MutableList`. While these
-methods would successfully update the collection previously, they would
-not correctly fire off change events. The operators mutate the collection
-as before but additionally emit the correct change event so that the change
-becomes part of the next flush process::
-
- model = session.query(MyModel).first()
- model.json_set &= {1, 3}
-
-:ticket:`3853`
-
-.. _change_3769:
-
-AssociationProxy any(), has(), contains() work with chained association proxies
--------------------------------------------------------------------------------
-
-The :meth:`.AssociationProxy.any`, :meth:`.AssociationProxy.has`
-and :meth:`.AssociationProxy.contains` comparison methods now support
-linkage to an attribute that is
-itself also an :class:`.AssociationProxy`, recursively. Below, ``A.b_values``
-is an association proxy that links to ``AtoB.bvalue``, which is
-itself an association proxy onto ``B``::
-
- class A(Base):
- __tablename__ = "a"
- id = Column(Integer, primary_key=True)
-
- b_values = association_proxy("atob", "b_value")
- c_values = association_proxy("atob", "c_value")
-
-
- class B(Base):
- __tablename__ = "b"
- id = Column(Integer, primary_key=True)
- a_id = Column(ForeignKey("a.id"))
- value = Column(String)
-
- c = relationship("C")
-
-
- class C(Base):
- __tablename__ = "c"
- id = Column(Integer, primary_key=True)
- b_id = Column(ForeignKey("b.id"))
- value = Column(String)
-
-
- class AtoB(Base):
- __tablename__ = "atob"
-
- a_id = Column(ForeignKey("a.id"), primary_key=True)
- b_id = Column(ForeignKey("b.id"), primary_key=True)
-
- a = relationship("A", backref="atob")
- b = relationship("B", backref="atob")
-
- b_value = association_proxy("b", "value")
- c_value = association_proxy("b", "c")
-
-We can query on ``A.b_values`` using :meth:`.AssociationProxy.contains` to
-query across the two proxies ``A.b_values``, ``AtoB.b_value``:
-
-.. sourcecode:: pycon+sql
-
- >>> s.query(A).filter(A.b_values.contains("hi")).all()
- {execsql}SELECT a.id AS a_id
- FROM a
- WHERE EXISTS (SELECT 1
- FROM atob
- WHERE a.id = atob.a_id AND (EXISTS (SELECT 1
- FROM b
- WHERE b.id = atob.b_id AND b.value = :value_1)))
-
-Similarly, we can query on ``A.c_values`` using :meth:`.AssociationProxy.any`
-to query across the two proxies ``A.c_values``, ``AtoB.c_value``:
-
-.. sourcecode:: pycon+sql
-
- >>> s.query(A).filter(A.c_values.any(value="x")).all()
- {execsql}SELECT a.id AS a_id
- FROM a
- WHERE EXISTS (SELECT 1
- FROM atob
- WHERE a.id = atob.a_id AND (EXISTS (SELECT 1
- FROM b
- WHERE b.id = atob.b_id AND (EXISTS (SELECT 1
- FROM c
- WHERE b.id = c.b_id AND c.value = :value_1)))))
-
-:ticket:`3769`
-
-.. _change_4137:
-
-Identity key enhancements to support sharding
----------------------------------------------
-
-The identity key structure used by the ORM now contains an additional
-member, so that two identical primary keys that originate from different
-contexts can co-exist within the same identity map.
-
-The example at :ref:`examples_sharding` has been updated to illustrate this
-behavior. The example shows a sharded class ``WeatherLocation`` that
-refers to a dependent ``WeatherReport`` object, where the ``WeatherReport``
-class is mapped to a table that stores a simple integer primary key. Two
-``WeatherReport`` objects from different databases may have the same
-primary key value. The example now illustrates that a new ``identity_token``
-field tracks this difference so that the two objects can co-exist in the
-same identity map::
-
- tokyo = WeatherLocation("Asia", "Tokyo")
- newyork = WeatherLocation("North America", "New York")
-
- tokyo.reports.append(Report(80.0))
- newyork.reports.append(Report(75))
-
- sess = create_session()
-
- sess.add_all([tokyo, newyork, quito])
-
- sess.commit()
-
- # the Report class uses a simple integer primary key. So across two
- # databases, a primary key will be repeated. The "identity_token" tracks
- # in memory that these two identical primary keys are local to different
- # databases.
-
- newyork_report = newyork.reports[0]
- tokyo_report = tokyo.reports[0]
-
- assert inspect(newyork_report).identity_key == (Report, (1,), "north_america")
- assert inspect(tokyo_report).identity_key == (Report, (1,), "asia")
-
- # the token representing the originating shard is also available directly
-
- assert inspect(newyork_report).identity_token == "north_america"
- assert inspect(tokyo_report).identity_token == "asia"
-
-:ticket:`4137`
-
-New Features and Improvements - Core
-====================================
-
-.. _change_4102:
-
-Boolean datatype now enforces strict True/False/None values
------------------------------------------------------------
-
-In version 1.1, the change described in :ref:`change_3730` produced an
-unintended side effect of altering the way :class:`.Boolean` behaves when
-presented with a non-integer value, such as a string. In particular, the
-string value ``"0"``, which would previously result in the value ``False``
-being generated, would now produce ``True``. Making matters worse, the change
-in behavior was only for some backends and not others, meaning code that sends
-string ``"0"`` values to :class:`.Boolean` would break inconsistently across
-backends.
-
-The ultimate solution to this problem is that **string values are not supported
-with Boolean**, so in 1.2 a hard ``TypeError`` is raised if a non-integer /
-True/False/None value is passed. Additionally, only the integer values
-0 and 1 are accepted.
-
-To accommodate for applications that wish to have more liberal interpretation
-of boolean values, the :class:`.TypeDecorator` should be used. Below
-illustrates a recipe that will allow for the "liberal" behavior of the pre-1.1
-:class:`.Boolean` datatype::
-
- from sqlalchemy import Boolean
- from sqlalchemy import TypeDecorator
-
-
- class LiberalBoolean(TypeDecorator):
- impl = Boolean
-
- def process_bind_param(self, value, dialect):
- if value is not None:
- value = bool(int(value))
- return value
-
-:ticket:`4102`
-
-.. _change_3919:
-
-Pessimistic disconnection detection added to the connection pool
-----------------------------------------------------------------
-
-The connection pool documentation has long featured a recipe for using
-the :meth:`_events.ConnectionEvents.engine_connect` engine event to emit a simple
-statement on a checked-out connection to test it for liveness. The
-functionality of this recipe has now been added into the connection pool
-itself, when used in conjunction with an appropriate dialect. Using
-the new parameter :paramref:`_sa.create_engine.pool_pre_ping`, each connection
-checked out will be tested for freshness before being returned::
-
- engine = create_engine("mysql+pymysql://", pool_pre_ping=True)
-
-While the "pre-ping" approach adds a small amount of latency to the connection
-pool checkout, for a typical application that is transactionally-oriented
-(which includes most ORM applications), this overhead is minimal, and
-eliminates the problem of acquiring a stale connection that will raise
-an error, requiring that the application either abandon or retry the operation.
-
-The feature does **not** accommodate for connections dropped within
-an ongoing transaction or SQL operation. If an application must recover
-from these as well, it would need to employ its own operation retry logic
-to anticipate these errors.
-
-
-.. seealso::
-
- :ref:`pool_disconnects_pessimistic`
-
-
-:ticket:`3919`
-
-.. _change_3907:
-
-The IN / NOT IN operator's empty collection behavior is now configurable; default expression simplified
--------------------------------------------------------------------------------------------------------
-
-An expression such as ``column.in_([])``, which is assumed to be false,
-now produces the expression ``1 != 1``
-by default, instead of ``column != column``. This will **change the result**
-of a query that is comparing a SQL expression or column that evaluates to
-NULL when compared to an empty set, producing a boolean value false or true
-(for NOT IN) rather than NULL. The warning that would emit under
-this condition is also removed. The old behavior is available using the
-:paramref:`_sa.create_engine.empty_in_strategy` parameter to
-:func:`_sa.create_engine`.
-
-In SQL, the IN and NOT IN operators do not support comparison to a
-collection of values that is explicitly empty; meaning, this syntax is
-illegal:
-
-.. sourcecode:: sql
-
- mycolumn IN ()
-
-To work around this, SQLAlchemy and other database libraries detect this
-condition and render an alternative expression that evaluates to false, or
-in the case of NOT IN, to true, based on the theory that "col IN ()" is always
-false since nothing is in "the empty set". Typically, in order to
-produce a false/true constant that is portable across databases and works
-in the context of the WHERE clause, a simple tautology such as ``1 != 1`` is
-used to evaluate to false and ``1 = 1`` to evaluate to true (a simple constant
-"0" or "1" often does not work as the target of a WHERE clause).
-
-SQLAlchemy in its early days began with this approach as well, but soon it
-was theorized that the SQL expression ``column IN ()`` would not evaluate to
-false if the "column" were NULL; instead, the expression would produce NULL,
-since "NULL" means "unknown", and comparisons to NULL in SQL usually produce
-NULL.
-
-To simulate this result, SQLAlchemy changed from using ``1 != 1`` to
-instead use th expression ``expr != expr`` for empty "IN" and ``expr = expr``
-for empty "NOT IN"; that is, instead of using a fixed value we use the
-actual left-hand side of the expression. If the left-hand side of
-the expression passed evaluates to NULL, then the comparison overall
-also gets the NULL result instead of false or true.
-
-Unfortunately, users eventually complained that this expression had a very
-severe performance impact on some query planners. At that point, a warning
-was added when an empty IN expression was encountered, favoring that SQLAlchemy
-continues to be "correct" and urging users to avoid code that generates empty
-IN predicates in general, since typically they can be safely omitted. However,
-this is of course burdensome in the case of queries that are built up dynamically
-from input variables, where an incoming set of values might be empty.
-
-In recent months, the original assumptions of this decision have been
-questioned. The notion that the expression "NULL IN ()" should return NULL was
-only theoretical, and could not be tested since databases don't support that
-syntax. However, as it turns out, you can in fact ask a relational database
-what value it would return for "NULL IN ()" by simulating the empty set as
-follows:
-
-.. sourcecode:: sql
-
- SELECT NULL IN (SELECT 1 WHERE 1 != 1)
-
-With the above test, we see that the databases themselves can't agree on
-the answer. PostgreSQL, considered by most to be the most "correct" database,
-returns False; because even though "NULL" represents "unknown", the "empty set"
-means nothing is present, including all unknown values. On the
-other hand, MySQL and MariaDB return NULL for the above expression, defaulting
-to the more common behavior of "all comparisons to NULL return NULL".
-
-SQLAlchemy's SQL architecture is more sophisticated than it was when this
-design decision was first made, so we can now allow either behavior to
-be invoked at SQL string compilation time. Previously, the conversion to a
-comparison expression were done at construction time, that is, the moment
-the :meth:`.ColumnOperators.in_` or :meth:`.ColumnOperators.notin_` operators were invoked.
-With the compilation-time behavior, the dialect itself can be instructed
-to invoke either approach, that is, the "static" ``1 != 1`` comparison or the
-"dynamic" ``expr != expr`` comparison. The default has been **changed**
-to be the "static" comparison, since this agrees with the behavior that
-PostgreSQL would have in any case and this is also what the vast majority
-of users prefer. This will **change the result** of a query that is comparing
-a null expression to the empty set, particularly one that is querying
-for the negation ``where(~null_expr.in_([]))``, since this now evaluates to true
-and not NULL.
-
-The behavior can now be controlled using the flag
-:paramref:`_sa.create_engine.empty_in_strategy`, which defaults to the
-``"static"`` setting, but may also be set to ``"dynamic"`` or
-``"dynamic_warn"``, where the ``"dynamic_warn"`` setting is equivalent to the
-previous behavior of emitting ``expr != expr`` as well as a performance
-warning. However, it is anticipated that most users will appreciate the
-"static" default.
-
-:ticket:`3907`
-
-.. _change_3953:
-
-Late-expanded IN parameter sets allow IN expressions with cached statements
----------------------------------------------------------------------------
-
-Added a new kind of :func:`.bindparam` called "expanding". This is
-for use in ``IN`` expressions where the list of elements is rendered
-into individual bound parameters at statement execution time, rather
-than at statement compilation time. This allows both a single bound
-parameter name to be linked to an IN expression of multiple elements,
-as well as allows query caching to be used with IN expressions. The
-new feature allows the related features of "select in" loading and
-"polymorphic in" loading to make use of the baked query extension
-to reduce call overhead::
-
- stmt = select([table]).where(table.c.col.in_(bindparam("foo", expanding=True)))
- conn.execute(stmt, {"foo": [1, 2, 3]})
-
-The feature should be regarded as **experimental** within the 1.2 series.
-
-
-:ticket:`3953`
-
-.. _change_3999:
-
-Flattened operator precedence for comparison operators
--------------------------------------------------------
-
-The operator precedence for operators like IN, LIKE, equals, IS, MATCH, and
-other comparison operators has been flattened into one level. This will
-have the effect of more parenthesization being generated when comparison
-operators are combined together, such as::
-
- (column("q") == null()) != (column("y") == null())
-
-Will now generate ``(q IS NULL) != (y IS NULL)`` rather than
-``q IS NULL != y IS NULL``.
-
-
-:ticket:`3999`
-
-.. _change_1546:
-
-Support for SQL Comments on Table, Column, includes DDL, reflection
--------------------------------------------------------------------
-
-The Core receives support for string comments associated with tables
-and columns. These are specified via the :paramref:`_schema.Table.comment` and
-:paramref:`_schema.Column.comment` arguments::
-
- Table(
- "my_table",
- metadata,
- Column("q", Integer, comment="the Q value"),
- comment="my Q table",
- )
-
-Above, DDL will be rendered appropriately upon table create to associate
-the above comments with the table/ column within the schema. When
-the above table is autoloaded or inspected with :meth:`_reflection.Inspector.get_columns`,
-the comments are included. The table comment is also available independently
-using the :meth:`_reflection.Inspector.get_table_comment` method.
-
-Current backend support includes MySQL, PostgreSQL, and Oracle.
-
-:ticket:`1546`
-
-.. _change_959:
-
-Multiple-table criteria support for DELETE
-------------------------------------------
-
-The :class:`_expression.Delete` construct now supports multiple-table criteria,
-implemented for those backends which support it, currently these are
-PostgreSQL, MySQL and Microsoft SQL Server (support is also added to the
-currently non-working Sybase dialect). The feature works in the same
-was as that of multiple-table criteria for UPDATE, first introduced in
-the 0.7 and 0.8 series.
-
-Given a statement as::
-
- stmt = (
- users.delete()
- .where(users.c.id == addresses.c.id)
- .where(addresses.c.email_address.startswith("ed%"))
- )
- conn.execute(stmt)
-
-The resulting SQL from the above statement on a PostgreSQL backend
-would render as:
-
-.. sourcecode:: sql
-
- DELETE FROM users USING addresses
- WHERE users.id = addresses.id
- AND (addresses.email_address LIKE %(email_address_1)s || '%%')
-
-.. seealso::
-
- :ref:`tutorial_multi_table_deletes`
-
-:ticket:`959`
-
-.. _change_2694:
-
-New "autoescape" option for startswith(), endswith()
-----------------------------------------------------
-
-The "autoescape" parameter is added to :meth:`.ColumnOperators.startswith`,
-:meth:`.ColumnOperators.endswith`, :meth:`.ColumnOperators.contains`.
-This parameter when set to ``True`` will automatically escape all occurrences
-of ``%``, ``_`` with an escape character, which defaults to a forwards slash ``/``;
-occurrences of the escape character itself are also escaped. The forwards slash
-is used to avoid conflicts with settings like PostgreSQL's
-``standard_confirming_strings``, whose default value changed as of PostgreSQL
-9.1, and MySQL's ``NO_BACKSLASH_ESCAPES`` settings. The existing "escape" parameter
-can now be used to change the autoescape character, if desired.
-
-.. note:: This feature has been changed as of 1.2.0 from its initial
- implementation in 1.2.0b2 such that autoescape is now passed as a boolean
- value, rather than a specific character to use as the escape character.
-
-An expression such as::
-
- >>> column("x").startswith("total%score", autoescape=True)
-
-Renders as:
-
-.. sourcecode:: sql
-
- x LIKE :x_1 || '%' ESCAPE '/'
-
-Where the value of the parameter "x_1" is ``'total/%score'``.
-
-Similarly, an expression that has backslashes::
-
- >>> column("x").startswith("total/score", autoescape=True)
-
-Will render the same way, with the value of the parameter "x_1" as
-``'total//score'``.
-
-
-:ticket:`2694`
-
-.. _change_floats_12:
-
-Stronger typing added to "float" datatypes
-------------------------------------------
-
-A series of changes allow for use of the :class:`.Float` datatype to more
-strongly link itself to Python floating point values, instead of the more
-generic :class:`.Numeric`. The changes are mostly related to ensuring
-that Python floating point values are not erroneously coerced to
-``Decimal()``, and are coerced to ``float`` if needed, on the result side,
-if the application is working with plain floats.
-
-* A plain Python "float" value passed to a SQL expression will now be
- pulled into a literal parameter with the type :class:`.Float`; previously,
- the type was :class:`.Numeric`, with the default "asdecimal=True" flag, which
- meant the result type would coerce to ``Decimal()``. In particular,
- this would emit a confusing warning on SQLite::
-
-
- float_value = connection.scalar(
- select([literal(4.56)]) # the "BindParameter" will now be
- # Float, not Numeric(asdecimal=True)
- )
-
-* Math operations between :class:`.Numeric`, :class:`.Float`, and
- :class:`.Integer` will now preserve the :class:`.Numeric` or :class:`.Float`
- type in the resulting expression's type, including the ``asdecimal`` flag
- as well as if the type should be :class:`.Float`::
-
- # asdecimal flag is maintained
- expr = column("a", Integer) * column("b", Numeric(asdecimal=False))
- assert expr.type.asdecimal == False
-
- # Float subclass of Numeric is maintained
- expr = column("a", Integer) * column("b", Float())
- assert isinstance(expr.type, Float)
-
-* The :class:`.Float` datatype will apply the ``float()`` processor to
- result values unconditionally if the DBAPI is known to support native
- ``Decimal()`` mode. Some backends do not always guarantee that a floating
- point number comes back as plain float and not precision numeric such
- as MySQL.
-
-:ticket:`4017`
-
-:ticket:`4018`
-
-:ticket:`4020`
-
-.. change_3249:
-
-Support for GROUPING SETS, CUBE, ROLLUP
----------------------------------------
-
-All three of GROUPING SETS, CUBE, ROLLUP are available via the
-:attr:`.func` namespace. In the case of CUBE and ROLLUP, these functions
-already work in previous versions, however for GROUPING SETS, a placeholder
-is added to the compiler to allow for the space. All three functions
-are named in the documentation now:
-
-.. sourcecode:: pycon+sql
-
- >>> from sqlalchemy import select, table, column, func, tuple_
- >>> t = table("t", column("value"), column("x"), column("y"), column("z"), column("q"))
- >>> stmt = select([func.sum(t.c.value)]).group_by(
- ... func.grouping_sets(
- ... tuple_(t.c.x, t.c.y),
- ... tuple_(t.c.z, t.c.q),
- ... )
- ... )
- >>> print(stmt)
- {printsql}SELECT sum(t.value) AS sum_1
- FROM t GROUP BY GROUPING SETS((t.x, t.y), (t.z, t.q))
-
-:ticket:`3429`
-
-.. _change_4075:
-
-Parameter helper for multi-valued INSERT with contextual default generator
---------------------------------------------------------------------------
-
-A default generation function, e.g. that described at
-:ref:`context_default_functions`, can look at the current parameters relevant
-to the statement via the :attr:`.DefaultExecutionContext.current_parameters`
-attribute. However, in the case of a :class:`_expression.Insert` construct that specifies
-multiple VALUES clauses via the :meth:`_expression.Insert.values` method, the user-defined
-function is called multiple times, once for each parameter set, however there
-was no way to know which subset of keys in
-:attr:`.DefaultExecutionContext.current_parameters` apply to that column. A
-new function :meth:`.DefaultExecutionContext.get_current_parameters` is added,
-which includes a keyword argument
-:paramref:`.DefaultExecutionContext.get_current_parameters.isolate_multiinsert_groups`
-defaulting to ``True``, which performs the extra work of delivering a sub-dictionary of
-:attr:`.DefaultExecutionContext.current_parameters` which has the names
-localized to the current VALUES clause being processed::
-
-
- def mydefault(context):
- return context.get_current_parameters()["counter"] + 12
-
-
- mytable = Table(
- "mytable",
- metadata_obj,
- Column("counter", Integer),
- Column("counter_plus_twelve", Integer, default=mydefault, onupdate=mydefault),
- )
-
- stmt = mytable.insert().values([{"counter": 5}, {"counter": 18}, {"counter": 20}])
-
- conn.execute(stmt)
-
-:ticket:`4075`
-
-Key Behavioral Changes - ORM
-============================
-
-.. _change_3934:
-
-The after_rollback() Session event now emits before the expiration of objects
------------------------------------------------------------------------------
-
-The :meth:`.SessionEvents.after_rollback` event now has access to the attribute
-state of objects before their state has been expired (e.g. the "snapshot
-removal"). This allows the event to be consistent with the behavior
-of the :meth:`.SessionEvents.after_commit` event which also emits before the
-"snapshot" has been removed::
-
- sess = Session()
-
- user = sess.query(User).filter_by(name="x").first()
-
-
- @event.listens_for(sess, "after_rollback")
- def after_rollback(session):
- # 'user.name' is now present, assuming it was already
- # loaded. previously this would raise upon trying
- # to emit a lazy load.
- print("user name: %s" % user.name)
-
-
- @event.listens_for(sess, "after_commit")
- def after_commit(session):
- # 'user.name' is present, assuming it was already
- # loaded. this is the existing behavior.
- print("user name: %s" % user.name)
-
-
- if should_rollback:
- sess.rollback()
- else:
- sess.commit()
-
-Note that the :class:`.Session` will still disallow SQL from being emitted
-within this event; meaning that unloaded attributes will still not be
-able to load within the scope of the event.
-
-:ticket:`3934`
-
-.. _change_3891:
-
-Fixed issue involving single-table inheritance with ``select_from()``
----------------------------------------------------------------------
-
-The :meth:`_query.Query.select_from` method now honors the single-table inheritance
-column discriminator when generating SQL; previously, only the expressions
-in the query column list would be taken into account.
-
-Supposing ``Manager`` is a subclass of ``Employee``. A query like the following::
-
- sess.query(Manager.id)
-
-Would generate SQL as:
-
-.. sourcecode:: sql
-
- SELECT employee.id FROM employee WHERE employee.type IN ('manager')
-
-However, if ``Manager`` were only specified by :meth:`_query.Query.select_from`
-and not in the columns list, the discriminator would not be added::
-
- sess.query(func.count(1)).select_from(Manager)
-
-would generate:
-
-.. sourcecode:: sql
-
- SELECT count(1) FROM employee
-
-With the fix, :meth:`_query.Query.select_from` now works correctly and we get:
-
-.. sourcecode:: sql
-
- SELECT count(1) FROM employee WHERE employee.type IN ('manager')
-
-Applications that may have been working around this by supplying the
-WHERE clause manually may need to be adjusted.
-
-:ticket:`3891`
-
-.. _change_3913:
-
-Previous collection is no longer mutated upon replacement
----------------------------------------------------------
-
-The ORM emits events whenever the members of a mapped collection change.
-In the case of assigning a collection to an attribute that would replace
-the previous collection, a side effect of this was that the collection
-being replaced would also be mutated, which is misleading and unnecessary::
-
- >>> a1, a2, a3 = Address("a1"), Address("a2"), Address("a3")
- >>> user.addresses = [a1, a2]
-
- >>> previous_collection = user.addresses
-
- # replace the collection with a new one
- >>> user.addresses = [a2, a3]
-
- >>> previous_collection
- [Address('a1'), Address('a2')]
-
-Above, prior to the change, the ``previous_collection`` would have had the
-"a1" member removed, corresponding to the member that's no longer in the
-new collection.
-
-:ticket:`3913`
-
-.. _change_3896_validates:
-
-A @validates method receives all values on bulk-collection set before comparison
---------------------------------------------------------------------------------
-
-A method that uses ``@validates`` will now receive all members of a collection
-during a "bulk set" operation, before comparison is applied against the
-existing collection.
-
-Given a mapping as::
-
- class A(Base):
- __tablename__ = "a"
- id = Column(Integer, primary_key=True)
- bs = relationship("B")
-
- @validates("bs")
- def convert_dict_to_b(self, key, value):
- return B(data=value["data"])
-
-
- class B(Base):
- __tablename__ = "b"
- id = Column(Integer, primary_key=True)
- a_id = Column(ForeignKey("a.id"))
- data = Column(String)
-
-Above, we could use the validator as follows, to convert from an incoming
-dictionary to an instance of ``B`` upon collection append::
-
- a1 = A()
- a1.bs.append({"data": "b1"})
-
-However, a collection assignment would fail, since the ORM would assume
-incoming objects are already instances of ``B`` as it attempts to compare them
-to the existing members of the collection, before doing collection appends
-which actually invoke the validator. This would make it impossible for bulk
-set operations to accommodate non-ORM objects like dictionaries that needed
-up-front modification::
-
- a1 = A()
- a1.bs = [{"data": "b1"}]
-
-The new logic uses the new :meth:`.AttributeEvents.bulk_replace` event to ensure
-that all values are sent to the ``@validates`` function up front.
-
-As part of this change, this means that validators will now receive
-**all** members of a collection upon bulk set, not just the members that
-are new. Supposing a simple validator such as::
-
- class A(Base):
- # ...
-
- @validates("bs")
- def validate_b(self, key, value):
- assert value.data is not None
- return value
-
-Above, if we began with a collection as::
-
- a1 = A()
-
- b1, b2 = B(data="one"), B(data="two")
- a1.bs = [b1, b2]
-
-And then, replaced the collection with one that overlaps the first::
-
- b3 = B(data="three")
- a1.bs = [b2, b3]
-
-Previously, the second assignment would trigger the ``A.validate_b``
-method only once, for the ``b3`` object. The ``b2`` object would be seen
-as being already present in the collection and not validated. With the new
-behavior, both ``b2`` and ``b3`` are passed to ``A.validate_b`` before passing
-onto the collection. It is thus important that validation methods employ
-idempotent behavior to suit such a case.
-
-.. seealso::
-
- :ref:`change_3896_event`
-
-:ticket:`3896`
-
-.. _change_3753:
-
-Use flag_dirty() to mark an object as "dirty" without any attribute changing
-----------------------------------------------------------------------------
-
-An exception is now raised if the :func:`.attributes.flag_modified` function
-is used to mark an attribute as modified that isn't actually loaded::
-
- a1 = A(data="adf")
- s.add(a1)
-
- s.flush()
-
- # expire, similarly as though we said s.commit()
- s.expire(a1, "data")
-
- # will raise InvalidRequestError
- attributes.flag_modified(a1, "data")
-
-This because the flush process will most likely fail in any case if the
-attribute remains un-present by the time flush occurs. To mark an object
-as "modified" without referring to any attribute specifically, so that it
-is considered within the flush process for the purpose of custom event handlers
-such as :meth:`.SessionEvents.before_flush`, use the new
-:func:`.attributes.flag_dirty` function::
-
- from sqlalchemy.orm import attributes
-
- attributes.flag_dirty(a1)
-
-:ticket:`3753`
-
-.. _change_3796:
-
-"scope" keyword removed from scoped_session
--------------------------------------------
-
-A very old and undocumented keyword argument ``scope`` has been removed::
-
- from sqlalchemy.orm import scoped_session
-
- Session = scoped_session(sessionmaker())
-
- session = Session(scope=None)
-
-The purpose of this keyword was an attempt to allow for variable
-"scopes", where ``None`` indicated "no scope" and would therefore return
-a new :class:`.Session`. The keyword has never been documented and will
-now raise ``TypeError`` if encountered. It is not anticipated that this
-keyword is in use, however if users report issues related to this during
-beta testing, it can be restored with a deprecation.
-
-:ticket:`3796`
-
-.. _change_3471:
-
-Refinements to post_update in conjunction with onupdate
--------------------------------------------------------
-
-A relationship that uses the :paramref:`_orm.relationship.post_update` feature
-will now interact better with a column that has an :paramref:`_schema.Column.onupdate`
-value set. If an object is inserted with an explicit value for the column,
-it is re-stated during the UPDATE so that the "onupdate" rule does not
-overwrite it::
-
- class A(Base):
- __tablename__ = "a"
- id = Column(Integer, primary_key=True)
- favorite_b_id = Column(ForeignKey("b.id", name="favorite_b_fk"))
- bs = relationship("B", primaryjoin="A.id == B.a_id")
- favorite_b = relationship(
- "B", primaryjoin="A.favorite_b_id == B.id", post_update=True
- )
- updated = Column(Integer, onupdate=my_onupdate_function)
-
-
- class B(Base):
- __tablename__ = "b"
- id = Column(Integer, primary_key=True)
- a_id = Column(ForeignKey("a.id", name="a_fk"))
-
-
- a1 = A()
- b1 = B()
-
- a1.bs.append(b1)
- a1.favorite_b = b1
- a1.updated = 5
- s.add(a1)
- s.flush()
-
-Above, the previous behavior would be that an UPDATE would emit after the
-INSERT, thus triggering the "onupdate" and overwriting the value
-"5". The SQL now looks like:
-
-.. sourcecode:: sql
-
- INSERT INTO a (favorite_b_id, updated) VALUES (?, ?)
- (None, 5)
- INSERT INTO b (a_id) VALUES (?)
- (1,)
- UPDATE a SET favorite_b_id=?, updated=? WHERE a.id = ?
- (1, 5, 1)
-
-Additionally, if the value of "updated" is *not* set, then we correctly
-get back the newly generated value on ``a1.updated``; previously, the logic
-that refreshes or expires the attribute to allow the generated value
-to be present would not fire off for a post-update. The
-:meth:`.InstanceEvents.refresh_flush` event is also emitted when a refresh
-within flush occurs in this case.
-
-:ticket:`3471`
-
-:ticket:`3472`
-
-.. _change_3496:
-
-post_update integrates with ORM versioning
-------------------------------------------
-
-The post_update feature, documented at :ref:`post_update`, involves that an
-UPDATE statement is emitted in response to changes to a particular
-relationship-bound foreign key, in addition to the INSERT/UPDATE/DELETE that
-would normally be emitted for the target row. This UPDATE statement
-now participates in the versioning feature, documented at
-:ref:`mapper_version_counter`.
-
-Given a mapping::
-
- class Node(Base):
- __tablename__ = "node"
- id = Column(Integer, primary_key=True)
- version_id = Column(Integer, default=0)
- parent_id = Column(ForeignKey("node.id"))
- favorite_node_id = Column(ForeignKey("node.id"))
-
- nodes = relationship("Node", primaryjoin=remote(parent_id) == id)
- favorite_node = relationship(
- "Node", primaryjoin=favorite_node_id == remote(id), post_update=True
- )
-
- __mapper_args__ = {"version_id_col": version_id}
-
-An UPDATE of a node that associates another node as "favorite" will
-now increment the version counter as well as match the current version::
-
- node = Node()
- session.add(node)
- session.commit() # node is now version #1
-
- node = session.query(Node).get(node.id)
- node.favorite_node = Node()
- session.commit() # node is now version #2
-
-Note that this means an object that receives an UPDATE in response to
-other attributes changing, and a second UPDATE due to a post_update
-relationship change, will now receive
-**two version counter updates for one flush**. However, if the object
-is subject to an INSERT within the current flush, the version counter
-**will not** be incremented an additional time, unless a server-side
-versioning scheme is in place.
-
-The reason post_update emits an UPDATE even for an UPDATE is now discussed at
-:ref:`faq_post_update_update`.
-
-.. seealso::
-
- :ref:`post_update`
-
- :ref:`faq_post_update_update`
-
-
-:ticket:`3496`
-
-Key Behavioral Changes - Core
-=============================
-
-.. _change_4063:
-
-The typing behavior of custom operators has been made consistent
-----------------------------------------------------------------
-
-User defined operators can be made on the fly using the
-:meth:`.Operators.op` function. Previously, the typing behavior of
-an expression against such an operator was inconsistent and also not
-controllable.
-
-Whereas in 1.1, an expression such as the following would produce
-a result with no return type (assume ``-%>`` is some special operator
-supported by the database)::
-
- >>> column("x", types.DateTime).op("-%>")(None).type
- NullType()
-
-Other types would use the default behavior of using the left-hand type
-as the return type::
-
- >>> column("x", types.String(50)).op("-%>")(None).type
- String(length=50)
-
-These behaviors were mostly by accident, so the behavior has been made
-consistent with the second form, that is the default return type is the
-same as the left-hand expression::
-
- >>> column("x", types.DateTime).op("-%>")(None).type
- DateTime()
-
-As most user-defined operators tend to be "comparison" operators, often
-one of the many special operators defined by PostgreSQL, the
-:paramref:`.Operators.op.is_comparison` flag has been repaired to follow
-its documented behavior of allowing the return type to be :class:`.Boolean`
-in all cases, including for :class:`_types.ARRAY` and :class:`_types.JSON`::
-
- >>> column("x", types.String(50)).op("-%>", is_comparison=True)(None).type
- Boolean()
- >>> column("x", types.ARRAY(types.Integer)).op("-%>", is_comparison=True)(None).type
- Boolean()
- >>> column("x", types.JSON()).op("-%>", is_comparison=True)(None).type
- Boolean()
-
-To assist with boolean comparison operators, a new shorthand method
-:meth:`.Operators.bool_op` has been added. This method should be preferred
-for on-the-fly boolean operators:
-
-.. sourcecode:: pycon+sql
-
- >>> print(column("x", types.Integer).bool_op("-%>")(5))
- {printsql}x -%> :x_1
-
-
-.. _change_3740:
-
-Percent signs in literal_column() now conditionally escaped
------------------------------------------------------------
-
-The :obj:`_expression.literal_column` construct now escapes percent sign characters
-conditionally, based on whether or not the DBAPI in use makes use of a
-percent-sign-sensitive paramstyle or not (e.g. 'format' or 'pyformat').
-
-Previously, it was not possible to produce a :obj:`_expression.literal_column`
-construct that stated a single percent sign:
-
-.. sourcecode:: pycon+sql
-
- >>> from sqlalchemy import literal_column
- >>> print(literal_column("some%symbol"))
- {printsql}some%%symbol
-
-The percent sign is now unaffected for dialects that are not set to
-use the 'format' or 'pyformat' paramstyles; dialects such most MySQL
-dialects which do state one of these paramstyles will continue to escape
-as is appropriate:
-
-.. sourcecode:: pycon+sql
-
- >>> from sqlalchemy import literal_column
- >>> print(literal_column("some%symbol"))
- {printsql}some%symbol{stop}
- >>> from sqlalchemy.dialects import mysql
- >>> print(literal_column("some%symbol").compile(dialect=mysql.dialect()))
- {printsql}some%%symbol{stop}
-
-As part of this change, the doubling that has been present when using
-operators like :meth:`.ColumnOperators.contains`,
-:meth:`.ColumnOperators.startswith` and :meth:`.ColumnOperators.endswith`
-is also refined to only occur when appropriate.
-
-:ticket:`3740`
-
-
-.. _change_3785:
-
-The column-level COLLATE keyword now quotes the collation name
---------------------------------------------------------------
-
-A bug in the :func:`_expression.collate` and :meth:`.ColumnOperators.collate`
-functions, used to supply ad-hoc column collations at the statement level,
-is fixed, where a case sensitive name would not be quoted::
-
- stmt = select([mytable.c.x, mytable.c.y]).order_by(
- mytable.c.somecolumn.collate("fr_FR")
- )
-
-now renders:
-
-.. sourcecode:: sql
-
- SELECT mytable.x, mytable.y,
- FROM mytable ORDER BY mytable.somecolumn COLLATE "fr_FR"
-
-Previously, the case sensitive name `"fr_FR"` would not be quoted. Currently,
-manual quoting of the "fr_FR" name is **not** detected, so applications that
-are manually quoting the identifier should be adjusted. Note that this change
-does not impact the use of collations at the type level (e.g. specified
-on the datatype like :class:`.String` at the table level), where quoting
-is already applied.
-
-:ticket:`3785`
-
-Dialect Improvements and Changes - PostgreSQL
-=============================================
-
-.. _change_4109:
-
-Support for Batch Mode / Fast Execution Helpers
-------------------------------------------------
-
-The psycopg2 ``cursor.executemany()`` method has been identified as performing
-poorly, particularly with INSERT statements. To alleviate this, psycopg2
-has added `Fast Execution Helpers <https://initd.org/psycopg/docs/extras.html#fast-execution-helpers>`_
-which rework statements into fewer server round trips by sending multiple
-DML statements in batch. SQLAlchemy 1.2 now includes support for these
-helpers to be used transparently whenever the :class:`_engine.Engine` makes use
-of ``cursor.executemany()`` to invoke a statement against multiple parameter
-sets. The feature is off by default and can be enabled using the
-``use_batch_mode`` argument on :func:`_sa.create_engine`::
-
- engine = create_engine(
- "postgresql+psycopg2://scott:tiger@host/dbname", use_batch_mode=True
- )
-
-The feature is considered to be experimental for the moment but may become
-on by default in a future release.
-
-.. seealso::
-
- :ref:`psycopg2_batch_mode`
-
-:ticket:`4109`
-
-.. _change_3959:
-
-Support for fields specification in INTERVAL, including full reflection
------------------------------------------------------------------------
-
-The "fields" specifier in PostgreSQL's INTERVAL datatype allows specification
-of which fields of the interval to store, including such values as "YEAR",
-"MONTH", "YEAR TO MONTH", etc. The :class:`_postgresql.INTERVAL` datatype
-now allows these values to be specified::
-
- from sqlalchemy.dialects.postgresql import INTERVAL
-
- Table("my_table", metadata, Column("some_interval", INTERVAL(fields="DAY TO SECOND")))
-
-Additionally, all INTERVAL datatypes can now be reflected independently
-of the "fields" specifier present; the "fields" parameter in the datatype
-itself will also be present::
-
- >>> inspect(engine).get_columns("my_table")
- [{'comment': None,
- 'name': u'some_interval', 'nullable': True,
- 'default': None, 'autoincrement': False,
- 'type': INTERVAL(fields=u'day to second')}]
-
-:ticket:`3959`
-
-Dialect Improvements and Changes - MySQL
-========================================
-
-.. _change_4009:
-
-Support for INSERT..ON DUPLICATE KEY UPDATE
--------------------------------------------
-
-The ``ON DUPLICATE KEY UPDATE`` clause of ``INSERT`` supported by MySQL
-is now supported using a MySQL-specific version of the
-:class:`_expression.Insert` object, via :func:`sqlalchemy.dialects.mysql.dml.insert`.
-This :class:`_expression.Insert` subclass adds a new method
-:meth:`~.mysql.dml.Insert.on_duplicate_key_update` that implements MySQL's syntax::
-
- from sqlalchemy.dialects.mysql import insert
-
- insert_stmt = insert(my_table).values(id="some_id", data="some data to insert")
-
- on_conflict_stmt = insert_stmt.on_duplicate_key_update(
- data=insert_stmt.inserted.data, status="U"
- )
-
- conn.execute(on_conflict_stmt)
-
-The above will render:
-
-.. sourcecode:: sql
-
- INSERT INTO my_table (id, data)
- VALUES (:id, :data)
- ON DUPLICATE KEY UPDATE data=VALUES(data), status=:status_1
-
-.. seealso::
-
- :ref:`mysql_insert_on_duplicate_key_update`
-
-:ticket:`4009`
-
-
-Dialect Improvements and Changes - Oracle
-=========================================
-
-.. _change_cxoracle_12:
-
-Major Refactor to cx_Oracle Dialect, Typing System
---------------------------------------------------
-
-With the introduction of the 6.x series of the cx_Oracle DBAPI, SQLAlchemy's
-cx_Oracle dialect has been reworked and simplified to take advantage of recent
-improvements in cx_Oracle as well as dropping support for patterns that were
-more relevant before the 5.x series of cx_Oracle.
-
-* The minimum cx_Oracle version supported is now 5.1.3; 5.3 or the most recent
- 6.x series are recommended.
-
-* The handling of datatypes has been refactored. The ``cursor.setinputsizes()``
- method is no longer used for any datatype except LOB types, per advice from
- cx_Oracle's developers. As a result, the parameters ``auto_setinputsizes``
- and ``exclude_setinputsizes`` are deprecated and no longer have any effect.
-
-* The ``coerce_to_decimal`` flag, when set to False to indicate that coercion
- of numeric types with precision and scale to ``Decimal`` should not occur,
- only impacts untyped (e.g. plain string with no :class:`.TypeEngine` objects)
- statements. A Core expression that includes a :class:`.Numeric` type or
- subtype will now follow the decimal coercion rules of that type.
-
-* The "two phase" transaction support in the dialect, already dropped for the
- 6.x series of cx_Oracle, has now been removed entirely as this feature has
- never worked correctly and is unlikely to have been in production use.
- As a result, the ``allow_twophase`` dialect flag is deprecated and also has no
- effect.
-
-* Fixed a bug involving the column keys present with RETURNING. Given
- a statement as follows::
-
- result = conn.execute(table.insert().values(x=5).returning(table.c.a, table.c.b))
-
- Previously, the keys in each row of the result would be ``ret_0`` and ``ret_1``,
- which are identifiers internal to the cx_Oracle RETURNING implementation.
- The keys will now be ``a`` and ``b`` as is expected for other dialects.
-
-* cx_Oracle's LOB datatype represents return values as a ``cx_Oracle.LOB``
- object, which is a cursor-associated proxy that returns the ultimate data
- value via a ``.read()`` method. Historically, if more rows were read before
- these LOB objects were consumed (specifically, more rows than the value of
- cursor.arraysize which causes a new batch of rows to be read), these LOB
- objects would raise the error "LOB variable no longer valid after subsequent
- fetch". SQLAlchemy worked around this by both automatically calling
- ``.read()`` upon these LOBs within its typing system, as well as using a
- special ``BufferedColumnResultSet`` which would ensure this data was buffered
- in case a call like ``cursor.fetchmany()`` or ``cursor.fetchall()`` were
- used.
-
- The dialect now makes use of a cx_Oracle outputtypehandler to handle these
- ``.read()`` calls, so that they are always called up front regardless of how
- many rows are being fetched, so that this error can no longer occur. As a
- result, the use of the ``BufferedColumnResultSet``, as well as some other
- internals to the Core ``ResultSet`` that were specific to this use case,
- have been removed. The type objects are also simplified as they no longer
- need to process a binary column result.
-
- Additionally, cx_Oracle 6.x has removed the conditions under which this error
- occurs in any case, so the error is no longer possible. The error
- can occur on SQLAlchemy in the case that the seldom (if ever) used
- ``auto_convert_lobs=False`` option is in use, in conjunction with the
- previous 5.x series of cx_Oracle, and more rows are read before the LOB
- objects can be consumed. Upgrading to cx_Oracle 6.x will resolve that issue.
-
-.. _change_4003:
-
-Oracle Unique, Check constraints now reflected
-----------------------------------------------
-
-UNIQUE and CHECK constraints now reflect via
-:meth:`_reflection.Inspector.get_unique_constraints` and
-:meth:`_reflection.Inspector.get_check_constraints`. A :class:`_schema.Table` object that's
-reflected will now include :class:`.CheckConstraint` objects as well.
-See the notes at :ref:`oracle_constraint_reflection` for information
-on behavioral quirks here, including that most :class:`_schema.Table` objects
-will still not include any :class:`.UniqueConstraint` objects as these
-usually represent via :class:`.Index`.
-
-.. seealso::
-
- :ref:`oracle_constraint_reflection`
-
-
-:ticket:`4003`
-
-.. _change_3276:
-
-Oracle foreign key constraint names are now "name normalized"
--------------------------------------------------------------
-
-The names of foreign key constraints as delivered to a
-:class:`_schema.ForeignKeyConstraint` object during table reflection as well as
-within the :meth:`_reflection.Inspector.get_foreign_keys` method will now be
-"name normalized", that is, expressed as lower case for a case insensitive
-name, rather than the raw UPPERCASE format that Oracle uses::
-
- >>> insp.get_indexes("addresses")
- [{'unique': False, 'column_names': [u'user_id'],
- 'name': u'address_idx', 'dialect_options': {}}]
-
- >>> insp.get_pk_constraint("addresses")
- {'name': u'pk_cons', 'constrained_columns': [u'id']}
-
- >>> insp.get_foreign_keys("addresses")
- [{'referred_table': u'users', 'referred_columns': [u'id'],
- 'referred_schema': None, 'name': u'user_id_fk',
- 'constrained_columns': [u'user_id']}]
-
-Previously, the foreign keys result would look like::
-
- [
- {
- "referred_table": "users",
- "referred_columns": ["id"],
- "referred_schema": None,
- "name": "USER_ID_FK",
- "constrained_columns": ["user_id"],
- }
- ]
-
-Where the above could create problems particularly with Alembic autogenerate.
-
-:ticket:`3276`
-
-
-Dialect Improvements and Changes - SQL Server
-=============================================
-
-.. _change_2626:
-
-SQL Server schema names with embedded dots supported
-----------------------------------------------------
-
-The SQL Server dialect has a behavior such that a schema name with a dot inside
-of it is assumed to be a "database"."owner" identifier pair, which is
-necessarily split up into these separate components during table and component
-reflection operations, as well as when rendering quoting for the schema name so
-that the two symbols are quoted separately. The schema argument can
-now be passed using brackets to manually specify where this split
-occurs, allowing database and/or owner names that themselves contain one
-or more dots::
-
- Table("some_table", metadata, Column("q", String(50)), schema="[MyDataBase.dbo]")
-
-The above table will consider the "owner" to be ``MyDataBase.dbo``, which
-will also be quoted upon render, and the "database" as None. To individually
-refer to database name and owner, use two pairs of brackets::
-
- Table(
- "some_table",
- metadata,
- Column("q", String(50)),
- schema="[MyDataBase.SomeDB].[MyDB.owner]",
- )
-
-Additionally, the :class:`.quoted_name` construct is now honored when
-passed to "schema" by the SQL Server dialect; the given symbol will
-not be split on the dot if the quote flag is True and will be interpreted
-as the "owner".
-
-.. seealso::
-
- :ref:`multipart_schema_names`
-
-:ticket:`2626`
-
-AUTOCOMMIT isolation level support
-----------------------------------
-
-Both the PyODBC and pymssql dialects now support the "AUTOCOMMIT" isolation
-level as set by :meth:`_engine.Connection.execution_options` which will establish
-the correct flags on the DBAPI connection object.
+++ /dev/null
-See doc/build/changelog/README.txt for
-information on the doc build system.
-
-
-DO NOT REMOVE THIS FILE!!!! THIS DIRECTORY MUST BE PRESENT
-FOR DOCS TO BUILD.
-
-
+++ /dev/null
-See doc/build/changelog/README.txt for
-information on the doc build system.
-
-
-DO NOT REMOVE THIS FILE!!!! THIS DIRECTORY MUST BE PRESENT
-FOR DOCS TO BUILD.
-
-
+++ /dev/null
-.. change::
- :tags: schema, bug
- :tickets: 6135
- :versions: 1.4.6
-
- The :class:`_schema.Table` object now raises an informative error message if
- it is instantiated without passing at least the :paramref:`_schema.Table.name`
- and :paramref:`_schema.Table.metadata` arguments positionally. Previously, if
- these were passed as keyword arguments, the object would silently fail to
- initialize correctly.
+++ /dev/null
-.. change::
- :tags: bug, postgresql, regression
- :tickets: 6182
- :versions: 1.4.5
-
- Fixed regression caused by :ticket:`6023` where the PostgreSQL cast
- operator applied to elements within an :class:`_types.ARRAY` when using
- psycopg2 would fail to use the correct type in the case that the datatype
- were also embedded within an instance of the :class:`_types.Variant`
- adapter.
-
- Additionally, repairs support for the correct CREATE TYPE to be emitted
- when using a ``Variant(ARRAY(some_schema_type))``.
+++ /dev/null
-.. change::
- :tags: bug, orm
- :tickets: 6392
- :versions: 1.4.12
-
- Fixed issue in :meth:`_orm.Session.bulk_save_objects` when used with persistent
- objects which would fail to track the primary key of mappings where the
- column name of the primary key were different than the attribute name.
-
+++ /dev/null
-.. change::
- :tags: bug, sqlite
- :tickets: 6589
- :versions: 1.4.18
-
- Add note regarding encryption-related pragmas for pysqlcipher passed in the
- url.
+++ /dev/null
-.. change::
- :tags: bug, mysql, mariadb
- :tickets: 7115, 7136
- :versions: 1.4.26
-
- Fixes to accommodate for the MariaDB 10.6 series, including backwards
- incompatible changes in both the mariadb-connector Python driver (supported
- on SQLAlchemy 1.4 only) as well as the native 10.6 client libraries that
- are used automatically by the mysqlclient DBAPI (applies to both 1.3 and
- 1.4). The "utf8mb3" encoding symbol is now reported by these client
- libraries when the encoding is stated as "utf8", leading to lookup and
- encoding errors within the MySQL dialect that does not expect this symbol.
- Updates to both the MySQL base library to accommodate for this utf8mb3
- symbol being reported as well as to the test suite. Thanks to Georg Richter
- for support.
-
+++ /dev/null
-See doc/build/changelog/README.txt for
-information on the doc build system.
-
-
-DO NOT REMOVE THIS FILE!!!! THIS DIRECTORY MUST BE PRESENT
-FOR DOCS TO BUILD.
-
-
:maxdepth: 2
event
- inspection
exceptions
internals
+++ /dev/null
-.. currentmodule:: sqlalchemy.schema
-
-.. _metadata_defaults_toplevel:
-
-.. _metadata_defaults:
-
-Column INSERT/UPDATE Defaults
-=============================
-
-Column INSERT and UPDATE defaults refer to functions that create a **default
-value** for a particular column in a row as an INSERT or UPDATE statement is
-proceeding against that row, in the case where **no value was provided to the
-INSERT or UPDATE statement for that column**. That is, if a table has a column
-called "timestamp", and an INSERT statement proceeds which does not include a
-value for this column, an INSERT default would create a new value, such as
-the current time, that is used as the value to be INSERTed into the "timestamp"
-column. If the statement *does* include a value for this column, then the
-default does *not* take place.
-
-Column defaults can be server-side functions or constant values which are
-defined in the database along with the schema in :term:`DDL`, or as SQL
-expressions which are rendered directly within an INSERT or UPDATE statement
-emitted by SQLAlchemy; they may also be client-side Python functions or
-constant values which are invoked by SQLAlchemy before data is passed to the
-database.
-
-.. note::
-
- A column default handler should not be confused with a construct that
- intercepts and modifies incoming values for INSERT and UPDATE statements
- which *are* provided to the statement as it is invoked. This is known
- as :term:`data marshalling`, where a column value is modified in some way
- by the application before being sent to the database. SQLAlchemy provides
- a few means of achieving this which include using :ref:`custom datatypes
- <types_typedecorator>`, :ref:`SQL execution events <core_sql_events>` and
- in the ORM :ref:`custom validators <simple_validators>` as well as
- :ref:`attribute events <orm_attribute_events>`. Column defaults are only
- invoked when there is **no value present** for a column in a SQL
- :term:`DML` statement.
-
-
-SQLAlchemy provides an array of features regarding default generation
-functions which take place for non-present values during INSERT and UPDATE
-statements. Options include:
-
-* Scalar values used as defaults during INSERT and UPDATE operations
-* Python functions which execute upon INSERT and UPDATE operations
-* SQL expressions which are embedded in INSERT statements (or in some cases execute beforehand)
-* SQL expressions which are embedded in UPDATE statements
-* Server side default values used during INSERT
-* Markers for server-side triggers used during UPDATE
-
-The general rule for all insert/update defaults is that they only take effect
-if no value for a particular column is passed as an ``execute()`` parameter;
-otherwise, the given value is used.
-
-Scalar Defaults
----------------
-
-The simplest kind of default is a scalar value used as the default value of a column::
-
- Table("mytable", metadata_obj, Column("somecolumn", Integer, default=12))
-
-Above, the value "12" will be bound as the column value during an INSERT if no
-other value is supplied.
-
-A scalar value may also be associated with an UPDATE statement, though this is
-not very common (as UPDATE statements are usually looking for dynamic
-defaults)::
-
- Table("mytable", metadata_obj, Column("somecolumn", Integer, onupdate=25))
-
-Python-Executed Functions
--------------------------
-
-The :paramref:`_schema.Column.default` and :paramref:`_schema.Column.onupdate` keyword arguments also accept Python
-functions. These functions are invoked at the time of insert or update if no
-other value for that column is supplied, and the value returned is used for
-the column's value. Below illustrates a crude "sequence" that assigns an
-incrementing counter to a primary key column::
-
- # a function which counts upwards
- i = 0
-
-
- def mydefault():
- global i
- i += 1
- return i
-
-
- t = Table(
- "mytable",
- metadata_obj,
- Column("id", Integer, primary_key=True, default=mydefault),
- )
-
-It should be noted that for real "incrementing sequence" behavior, the
-built-in capabilities of the database should normally be used, which may
-include sequence objects or other autoincrementing capabilities. For primary
-key columns, SQLAlchemy will in most cases use these capabilities
-automatically. See the API documentation for
-:class:`~sqlalchemy.schema.Column` including the :paramref:`_schema.Column.autoincrement` flag, as
-well as the section on :class:`~sqlalchemy.schema.Sequence` later in this
-chapter for background on standard primary key generation techniques.
-
-To illustrate onupdate, we assign the Python ``datetime`` function ``now`` to
-the :paramref:`_schema.Column.onupdate` attribute::
-
- import datetime
-
- t = Table(
- "mytable",
- metadata_obj,
- Column("id", Integer, primary_key=True),
- # define 'last_updated' to be populated with datetime.now()
- Column("last_updated", DateTime, onupdate=datetime.datetime.now),
- )
-
-When an update statement executes and no value is passed for ``last_updated``,
-the ``datetime.datetime.now()`` Python function is executed and its return
-value used as the value for ``last_updated``. Notice that we provide ``now``
-as the function itself without calling it (i.e. there are no parenthesis
-following) - SQLAlchemy will execute the function at the time the statement
-executes.
-
-.. _context_default_functions:
-
-Context-Sensitive Default Functions
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The Python functions used by :paramref:`_schema.Column.default` and
-:paramref:`_schema.Column.onupdate` may also make use of the current statement's
-context in order to determine a value. The `context` of a statement is an
-internal SQLAlchemy object which contains all information about the statement
-being executed, including its source expression, the parameters associated with
-it and the cursor. The typical use case for this context with regards to
-default generation is to have access to the other values being inserted or
-updated on the row. To access the context, provide a function that accepts a
-single ``context`` argument::
-
- def mydefault(context):
- return context.get_current_parameters()["counter"] + 12
-
-
- t = Table(
- "mytable",
- metadata_obj,
- Column("counter", Integer),
- Column("counter_plus_twelve", Integer, default=mydefault, onupdate=mydefault),
- )
-
-The above default generation function is applied so that it will execute for
-all INSERT and UPDATE statements where a value for ``counter_plus_twelve`` was
-otherwise not provided, and the value will be that of whatever value is present
-in the execution for the ``counter`` column, plus the number 12.
-
-For a single statement that is being executed using "executemany" style, e.g.
-with multiple parameter sets passed to :meth:`_engine.Connection.execute`, the
-user-defined function is called once for each set of parameters. For the use case of
-a multi-valued :class:`_expression.Insert` construct (e.g. with more than one VALUES
-clause set up via the :meth:`_expression.Insert.values` method), the user-defined function
-is also called once for each set of parameters.
-
-When the function is invoked, the special method
-:meth:`.DefaultExecutionContext.get_current_parameters` is available from
-the context object (an subclass of :class:`.DefaultExecutionContext`). This
-method returns a dictionary of column-key to values that represents the
-full set of values for the INSERT or UPDATE statement. In the case of a
-multi-valued INSERT construct, the subset of parameters that corresponds to
-the individual VALUES clause is isolated from the full parameter dictionary
-and returned alone.
-
-.. versionadded:: 1.2
-
- Added :meth:`.DefaultExecutionContext.get_current_parameters` method,
- which improves upon the still-present
- :attr:`.DefaultExecutionContext.current_parameters` attribute
- by offering the service of organizing multiple VALUES clauses
- into individual parameter dictionaries.
-
-.. _defaults_client_invoked_sql:
-
-Client-Invoked SQL Expressions
-------------------------------
-
-The :paramref:`_schema.Column.default` and :paramref:`_schema.Column.onupdate` keywords may
-also be passed SQL expressions, which are in most cases rendered inline within the
-INSERT or UPDATE statement::
-
- t = Table(
- "mytable",
- metadata_obj,
- Column("id", Integer, primary_key=True),
- # define 'create_date' to default to now()
- Column("create_date", DateTime, default=func.now()),
- # define 'key' to pull its default from the 'keyvalues' table
- Column(
- "key",
- String(20),
- default=select(keyvalues.c.key).where(keyvalues.c.type="type1"),
- ),
- # define 'last_modified' to use the current_timestamp SQL function on update
- Column("last_modified", DateTime, onupdate=func.utc_timestamp()),
- )
-
-Above, the ``create_date`` column will be populated with the result of the
-``now()`` SQL function (which, depending on backend, compiles into ``NOW()``
-or ``CURRENT_TIMESTAMP`` in most cases) during an INSERT statement, and the
-``key`` column with the result of a SELECT subquery from another table. The
-``last_modified`` column will be populated with the value of
-the SQL ``UTC_TIMESTAMP()`` MySQL function when an UPDATE statement is
-emitted for this table.
-
-.. note::
-
- When using SQL functions with the :attr:`.func` construct, we "call" the
- named function, e.g. with parenthesis as in ``func.now()``. This differs
- from when we specify a Python callable as a default such as
- ``datetime.datetime``, where we pass the function itself, but we don't
- invoke it ourselves. In the case of a SQL function, invoking
- ``func.now()`` returns the SQL expression object that will render the
- "NOW" function into the SQL being emitted.
-
-Default and update SQL expressions specified by :paramref:`_schema.Column.default` and
-:paramref:`_schema.Column.onupdate` are invoked explicitly by SQLAlchemy when an
-INSERT or UPDATE statement occurs, typically rendered inline within the DML
-statement except in certain cases listed below. This is different than a
-"server side" default, which is part of the table's DDL definition, e.g. as
-part of the "CREATE TABLE" statement, which are likely more common. For
-server side defaults, see the next section :ref:`server_defaults`.
-
-When a SQL expression indicated by :paramref:`_schema.Column.default` is used with
-primary key columns, there are some cases where SQLAlchemy must "pre-execute"
-the default generation SQL function, meaning it is invoked in a separate SELECT
-statement, and the resulting value is passed as a parameter to the INSERT.
-This only occurs for primary key columns for an INSERT statement that is being
-asked to return this primary key value, where RETURNING or ``cursor.lastrowid``
-may not be used. An :class:`_expression.Insert` construct that specifies the
-:paramref:`~.expression.insert.inline` flag will always render default expressions
-inline.
-
-When the statement is executed with a single set of parameters (that is, it is
-not an "executemany" style execution), the returned
-:class:`~sqlalchemy.engine.CursorResult` will contain a collection accessible
-via :meth:`_engine.CursorResult.postfetch_cols` which contains a list of all
-:class:`~sqlalchemy.schema.Column` objects which had an inline-executed
-default. Similarly, all parameters which were bound to the statement, including
-all Python and SQL expressions which were pre-executed, are present in the
-:meth:`_engine.CursorResult.last_inserted_params` or
-:meth:`_engine.CursorResult.last_updated_params` collections on
-:class:`~sqlalchemy.engine.CursorResult`. The
-:attr:`_engine.CursorResult.inserted_primary_key` collection contains a list of primary
-key values for the row inserted (a list so that single-column and
-composite-column primary keys are represented in the same format).
-
-.. _server_defaults:
-
-Server-invoked DDL-Explicit Default Expressions
------------------------------------------------
-
-A variant on the SQL expression default is the :paramref:`_schema.Column.server_default`, which gets
-placed in the CREATE TABLE statement during a :meth:`_schema.Table.create` operation:
-
-.. sourcecode:: python+sql
-
- t = Table(
- "test",
- metadata_obj,
- Column("abc", String(20), server_default="abc"),
- Column("created_at", DateTime, server_default=func.sysdate()),
- Column("index_value", Integer, server_default=text("0")),
- )
-
-A create call for the above table will produce:
-
-.. sourcecode:: sql
-
- CREATE TABLE test (
- abc varchar(20) default 'abc',
- created_at datetime default sysdate,
- index_value integer default 0
- )
-
-The above example illustrates the two typical use cases for :paramref:`_schema.Column.server_default`,
-that of the SQL function (SYSDATE in the above example) as well as a server-side constant
-value (the integer "0" in the above example). It is advisable to use the
-:func:`_expression.text` construct for any literal SQL values as opposed to passing the
-raw value, as SQLAlchemy does not typically perform any quoting or escaping on
-these values.
-
-Like client-generated expressions, :paramref:`_schema.Column.server_default` can accommodate
-SQL expressions in general, however it is expected that these will usually be simple
-functions and expressions, and not the more complex cases like an embedded SELECT.
-
-
-.. _triggered_columns:
-
-Marking Implicitly Generated Values, timestamps, and Triggered Columns
-----------------------------------------------------------------------
-
-Columns which generate a new value on INSERT or UPDATE based on other
-server-side database mechanisms, such as database-specific auto-generating
-behaviors such as seen with TIMESTAMP columns on some platforms, as well as
-custom triggers that invoke upon INSERT or UPDATE to generate a new value,
-may be called out using :class:`.FetchedValue` as a marker::
-
- from sqlalchemy.schema import FetchedValue
-
- t = Table(
- "test",
- metadata_obj,
- Column("id", Integer, primary_key=True),
- Column("abc", TIMESTAMP, server_default=FetchedValue()),
- Column("def", String(20), server_onupdate=FetchedValue()),
- )
-
-The :class:`.FetchedValue` indicator does not affect the rendered DDL for the
-CREATE TABLE. Instead, it marks the column as one that will have a new value
-populated by the database during the process of an INSERT or UPDATE statement,
-and for supporting databases may be used to indicate that the column should be
-part of a RETURNING or OUTPUT clause for the statement. Tools such as the
-SQLAlchemy ORM then make use of this marker in order to know how to get at the
-value of the column after such an operation. In particular, the
-:meth:`.ValuesBase.return_defaults` method can be used with an :class:`_expression.Insert`
-or :class:`_expression.Update` construct to indicate that these values should be
-returned.
-
-For details on using :class:`.FetchedValue` with the ORM, see
-:ref:`orm_server_defaults`.
-
-.. warning:: The :paramref:`_schema.Column.server_onupdate` directive
- **does not** currently produce MySQL's
- "ON UPDATE CURRENT_TIMESTAMP()" clause. See
- :ref:`mysql_timestamp_onupdate` for background on how to produce
- this clause.
-
-
-.. seealso::
-
- :ref:`orm_server_defaults`
-
-.. _defaults_sequences:
-
-Defining Sequences
-------------------
-
-SQLAlchemy represents database sequences using the
-:class:`~sqlalchemy.schema.Sequence` object, which is considered to be a
-special case of "column default". It only has an effect on databases which have
-explicit support for sequences, which among SQLAlchemy's included dialects
-includes PostgreSQL, Oracle, MS SQL Server, and MariaDB. The
-:class:`~sqlalchemy.schema.Sequence` object is otherwise ignored.
-
-.. tip::
-
- In newer database engines, the :class:`.Identity` construct should likely
- be preferred vs. :class:`.Sequence` for generation of integer primary key
- values. See the section :ref:`identity_ddl` for background on this
- construct.
-
-The :class:`~sqlalchemy.schema.Sequence` may be placed on any column as a
-"default" generator to be used during INSERT operations, and can also be
-configured to fire off during UPDATE operations if desired. It is most
-commonly used in conjunction with a single integer primary key column::
-
- table = Table(
- "cartitems",
- metadata_obj,
- Column(
- "cart_id",
- Integer,
- Sequence("cart_id_seq", start=1),
- primary_key=True,
- ),
- Column("description", String(40)),
- Column("createdate", DateTime()),
- )
-
-Where above, the table ``cartitems`` is associated with a sequence named
-``cart_id_seq``. Emitting :meth:`.MetaData.create_all` for the above
-table will include:
-
-.. sourcecode:: sql
-
- CREATE SEQUENCE cart_id_seq START WITH 1
-
- CREATE TABLE cartitems (
- cart_id INTEGER NOT NULL,
- description VARCHAR(40),
- createdate TIMESTAMP WITHOUT TIME ZONE,
- PRIMARY KEY (cart_id)
- )
-
-.. tip::
-
- When using tables with explicit schema names (detailed at
- :ref:`schema_table_schema_name`), the configured schema of the :class:`.Table`
- is **not** automatically shared by an embedded :class:`.Sequence`, instead,
- specify :paramref:`.Sequence.schema`::
-
- Sequence("cart_id_seq", start=1, schema="some_schema")
-
- The :class:`.Sequence` may also be made to automatically make use of the
- :paramref:`.MetaData.schema` setting on the :class:`.MetaData` in use;
- see :ref:`sequence_metadata` for background.
-
-When :class:`_dml.Insert` DML constructs are invoked against the ``cartitems``
-table, without an explicit value passed for the ``cart_id`` column, the
-``cart_id_seq`` sequence will be used to generate a value on participating
-backends. Typically, the sequence function is embedded in the INSERT statement,
-which is combined with RETURNING so that the newly generated value can be
-returned to the Python process:
-
-.. sourcecode:: sql
-
- INSERT INTO cartitems (cart_id, description, createdate)
- VALUES (next_val(cart_id_seq), 'some description', '2015-10-15 12:00:15')
- RETURNING cart_id
-
-When using :meth:`.Connection.execute` to invoke an :class:`_dml.Insert` construct,
-newly generated primary key identifiers, including but not limited to those
-generated using :class:`.Sequence`, are available from the :class:`.CursorResult`
-construct using the :attr:`.CursorResult.inserted_primary_key` attribute.
-
-When the :class:`~sqlalchemy.schema.Sequence` is associated with a
-:class:`_schema.Column` as its **Python-side** default generator, the
-:class:`.Sequence` will also be subject to "CREATE SEQUENCE" and "DROP
-SEQUENCE" DDL when similar DDL is emitted for the owning :class:`_schema.Table`,
-such as when using :meth:`.MetaData.create_all` to generate DDL for a series
-of tables.
-
-The :class:`.Sequence` may also be associated with a
-:class:`.MetaData` construct directly. This allows the :class:`.Sequence`
-to be used in more than one :class:`.Table` at a time and also allows the
-:paramref:`.MetaData.schema` parameter to be inherited. See the section
-:ref:`sequence_metadata` for background.
-
-Associating a Sequence on a SERIAL column
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-PostgreSQL's SERIAL datatype is an auto-incrementing type that implies
-the implicit creation of a PostgreSQL sequence when CREATE TABLE is emitted.
-The :class:`.Sequence` construct, when indicated for a :class:`_schema.Column`,
-may indicate that it should not be used in this specific case by specifying
-a value of ``True`` for the :paramref:`.Sequence.optional` parameter.
-This allows the given :class:`.Sequence` to be used for backends that have no
-alternative primary key generation system but to ignore it for backends
-such as PostgreSQL which will automatically generate a sequence for a particular
-column::
-
- table = Table(
- "cartitems",
- metadata_obj,
- Column(
- "cart_id",
- Integer,
- # use an explicit Sequence where available, but not on
- # PostgreSQL where SERIAL will be used
- Sequence("cart_id_seq", start=1, optional=True),
- primary_key=True,
- ),
- Column("description", String(40)),
- Column("createdate", DateTime()),
- )
-
-In the above example, ``CREATE TABLE`` for PostgreSQL will make use of the
-``SERIAL`` datatype for the ``cart_id`` column, and the ``cart_id_seq``
-sequence will be ignored. However on Oracle, the ``cart_id_seq`` sequence
-will be created explicitly.
-
-.. tip::
-
- This particular interaction of SERIAL and SEQUENCE is fairly legacy, and
- as in other cases, using :class:`.Identity` instead will simplify the
- operation to simply use ``IDENTITY`` on all supported backends.
-
-
-Executing a Sequence Standalone
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-A SEQUENCE is a first class schema object in SQL and can be used to generate
-values independently in the database. If you have a :class:`.Sequence`
-object, it can be invoked with its "next value" instruction by
-passing it directly to a SQL execution method::
-
- with my_engine.connect() as conn:
- seq = Sequence("some_sequence", start=1)
- nextid = conn.execute(seq)
-
-In order to embed the "next value" function of a :class:`.Sequence`
-inside of a SQL statement like a SELECT or INSERT, use the :meth:`.Sequence.next_value`
-method, which will render at statement compilation time a SQL function that is
-appropriate for the target backend:
-
-.. sourcecode:: pycon+sql
-
- >>> my_seq = Sequence("some_sequence", start=1)
- >>> stmt = select(my_seq.next_value())
- >>> print(stmt.compile(dialect=postgresql.dialect()))
- {printsql}SELECT nextval('some_sequence') AS next_value_1
-
-.. _sequence_metadata:
-
-Associating a Sequence with the MetaData
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-For a :class:`.Sequence` that is to be associated with arbitrary
-:class:`.Table` objects, the :class:`.Sequence` may be associated with
-a particular :class:`_schema.MetaData`, using the
-:paramref:`.Sequence.metadata` parameter::
-
- seq = Sequence("my_general_seq", metadata=metadata_obj, start=1)
-
-Such a sequence can then be associated with columns in the usual way::
-
- table = Table(
- "cartitems",
- metadata_obj,
- seq,
- Column("description", String(40)),
- Column("createdate", DateTime()),
- )
-
-In the above example, the :class:`.Sequence` object is treated as an
-independent schema construct that can exist on its own or be shared among
-tables.
-
-Explicitly associating the :class:`.Sequence` with :class:`_schema.MetaData`
-allows for the following behaviors:
-
-* The :class:`.Sequence` will inherit the :paramref:`_schema.MetaData.schema`
- parameter specified to the target :class:`_schema.MetaData`, which
- affects the production of CREATE / DROP DDL as well as how the
- :meth:`.Sequence.next_value` function is rendered in SQL statements.
-
-* The :meth:`_schema.MetaData.create_all` and :meth:`_schema.MetaData.drop_all`
- methods will emit CREATE / DROP for this :class:`.Sequence`,
- even if the :class:`.Sequence` is not associated with any
- :class:`_schema.Table` / :class:`_schema.Column` that's a member of this
- :class:`_schema.MetaData`.
-
-Associating a Sequence as the Server Side Default
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. note:: The following technique is known to work only with the PostgreSQL
- database. It does not work with Oracle.
-
-The preceding sections illustrate how to associate a :class:`.Sequence` with a
-:class:`_schema.Column` as the **Python side default generator**::
-
- Column(
- "cart_id",
- Integer,
- Sequence("cart_id_seq", metadata=metadata_obj, start=1),
- primary_key=True,
- )
-
-In the above case, the :class:`.Sequence` will automatically be subject
-to CREATE SEQUENCE / DROP SEQUENCE DDL when the related :class:`_schema.Table`
-is subject to CREATE / DROP. However, the sequence will **not** be present
-as the server-side default for the column when CREATE TABLE is emitted.
-
-If we want the sequence to be used as a server-side default,
-meaning it takes place even if we emit INSERT commands to the table from
-the SQL command line, we can use the :paramref:`_schema.Column.server_default`
-parameter in conjunction with the value-generation function of the
-sequence, available from the :meth:`.Sequence.next_value` method. Below
-we illustrate the same :class:`.Sequence` being associated with the
-:class:`_schema.Column` both as the Python-side default generator as well as
-the server-side default generator::
-
- cart_id_seq = Sequence("cart_id_seq", metadata=metadata_obj, start=1)
- table = Table(
- "cartitems",
- metadata_obj,
- Column(
- "cart_id",
- Integer,
- cart_id_seq,
- server_default=cart_id_seq.next_value(),
- primary_key=True,
- ),
- Column("description", String(40)),
- Column("createdate", DateTime()),
- )
-
-or with the ORM::
-
- class CartItem(Base):
- __tablename__ = "cartitems"
-
- cart_id_seq = Sequence("cart_id_seq", metadata=Base.metadata, start=1)
- cart_id = Column(
- Integer, cart_id_seq, server_default=cart_id_seq.next_value(), primary_key=True
- )
- description = Column(String(40))
- createdate = Column(DateTime)
-
-When the "CREATE TABLE" statement is emitted, on PostgreSQL it would be
-emitted as:
-
-.. sourcecode:: sql
-
- CREATE TABLE cartitems (
- cart_id INTEGER DEFAULT nextval('cart_id_seq') NOT NULL,
- description VARCHAR(40),
- createdate TIMESTAMP WITHOUT TIME ZONE,
- PRIMARY KEY (cart_id)
- )
-
-Placement of the :class:`.Sequence` in both the Python-side and server-side
-default generation contexts ensures that the "primary key fetch" logic
-works in all cases. Typically, sequence-enabled databases also support
-RETURNING for INSERT statements, which is used automatically by SQLAlchemy
-when emitting this statement. However if RETURNING is not used for a particular
-insert, then SQLAlchemy would prefer to "pre-execute" the sequence outside
-of the INSERT statement itself, which only works if the sequence is
-included as the Python-side default generator function.
-
-The example also associates the :class:`.Sequence` with the enclosing
-:class:`_schema.MetaData` directly, which again ensures that the :class:`.Sequence`
-is fully associated with the parameters of the :class:`_schema.MetaData` collection
-including the default schema, if any.
-
-.. seealso::
-
- :ref:`postgresql_sequences` - in the PostgreSQL dialect documentation
-
- :ref:`oracle_returning` - in the Oracle dialect documentation
-
-.. _computed_ddl:
-
-Computed Columns (GENERATED ALWAYS AS)
---------------------------------------
-
-.. versionadded:: 1.3.11
-
-The :class:`.Computed` construct allows a :class:`_schema.Column` to be declared in
-DDL as a "GENERATED ALWAYS AS" column, that is, one which has a value that is
-computed by the database server. The construct accepts a SQL expression
-typically declared textually using a string or the :func:`_expression.text` construct, in
-a similar manner as that of :class:`.CheckConstraint`. The SQL expression is
-then interpreted by the database server in order to determine the value for the
-column within a row.
-
-Example::
-
- from sqlalchemy import Table, Column, MetaData, Integer, Computed
-
- metadata_obj = MetaData()
-
- square = Table(
- "square",
- metadata_obj,
- Column("id", Integer, primary_key=True),
- Column("side", Integer),
- Column("area", Integer, Computed("side * side")),
- Column("perimeter", Integer, Computed("4 * side")),
- )
-
-The DDL for the ``square`` table when run on a PostgreSQL 12 backend will look
-like:
-
-.. sourcecode:: sql
-
- CREATE TABLE square (
- id SERIAL NOT NULL,
- side INTEGER,
- area INTEGER GENERATED ALWAYS AS (side * side) STORED,
- perimeter INTEGER GENERATED ALWAYS AS (4 * side) STORED,
- PRIMARY KEY (id)
- )
-
-Whether the value is persisted upon INSERT and UPDATE, or if it is calculated
-on fetch, is an implementation detail of the database; the former is known as
-"stored" and the latter is known as "virtual". Some database implementations
-support both, but some only support one or the other. The optional
-:paramref:`.Computed.persisted` flag may be specified as ``True`` or ``False``
-to indicate if the "STORED" or "VIRTUAL" keyword should be rendered in DDL,
-however this will raise an error if the keyword is not supported by the target
-backend; leaving it unset will use a working default for the target backend.
-
-The :class:`.Computed` construct is a subclass of the :class:`.FetchedValue`
-object, and will set itself up as both the "server default" and "server
-onupdate" generator for the target :class:`_schema.Column`, meaning it will be treated
-as a default generating column when INSERT and UPDATE statements are generated,
-as well as that it will be fetched as a generating column when using the ORM.
-This includes that it will be part of the RETURNING clause of the database
-for databases which support RETURNING and the generated values are to be
-eagerly fetched.
-
-.. note:: A :class:`_schema.Column` that is defined with the :class:`.Computed`
- construct may not store any value outside of that which the server applies
- to it; SQLAlchemy's behavior when a value is passed for such a column
- to be written in INSERT or UPDATE is currently that the value will be
- ignored.
-
-"GENERATED ALWAYS AS" is currently known to be supported by:
-
-* MySQL version 5.7 and onwards
-
-* MariaDB 10.x series and onwards
-
-* PostgreSQL as of version 12
-
-* Oracle - with the caveat that RETURNING does not work correctly with UPDATE
- (a warning will be emitted to this effect when the UPDATE..RETURNING that
- includes a computed column is rendered)
-
-* Microsoft SQL Server
-
-* SQLite as of version 3.31
-
-When :class:`.Computed` is used with an unsupported backend, if the target
-dialect does not support it, a :class:`.CompileError` is raised when attempting
-to render the construct. Otherwise, if the dialect supports it but the
-particular database server version in use does not, then a subclass of
-:class:`.DBAPIError`, usually :class:`.OperationalError`, is raised when the
-DDL is emitted to the database.
-
-.. seealso::
-
- :class:`.Computed`
-
-.. _identity_ddl:
-
-Identity Columns (GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY)
------------------------------------------------------------------
-
-.. versionadded:: 1.4
-
-The :class:`.Identity` construct allows a :class:`_schema.Column` to be declared
-as an identity column and rendered in DDL as "GENERATED { ALWAYS | BY DEFAULT }
-AS IDENTITY". An identity column has its value automatically generated by the
-database server using an incrementing (or decrementing) sequence. The construct
-shares most of its option to control the database behaviour with
-:class:`.Sequence`.
-
-Example::
-
- from sqlalchemy import Table, Column, MetaData, Integer, Identity, String
-
- metadata_obj = MetaData()
-
- data = Table(
- "data",
- metadata_obj,
- Column("id", Integer, Identity(start=42, cycle=True), primary_key=True),
- Column("data", String),
- )
-
-The DDL for the ``data`` table when run on a PostgreSQL 12 backend will look
-like:
-
-.. sourcecode:: sql
-
- CREATE TABLE data (
- id INTEGER GENERATED BY DEFAULT AS IDENTITY (START WITH 42 CYCLE) NOT NULL,
- data VARCHAR,
- PRIMARY KEY (id)
- )
-
-The database will generate a value for the ``id`` column upon insert,
-starting from ``42``, if the statement did not already contain a value for
-the ``id`` column.
-An identity column can also require that the database generates the value
-of the column, ignoring the value passed with the statement or raising an
-error, depending on the backend. To activate this mode, set the parameter
-:paramref:`_schema.Identity.always` to ``True`` in the
-:class:`.Identity` construct. Updating the previous
-example to include this parameter will generate the following DDL:
-
-.. sourcecode:: sql
-
- CREATE TABLE data (
- id INTEGER GENERATED ALWAYS AS IDENTITY (START WITH 42 CYCLE) NOT NULL,
- data VARCHAR,
- PRIMARY KEY (id)
- )
-
-The :class:`.Identity` construct is a subclass of the :class:`.FetchedValue`
-object, and will set itself up as the "server default" generator for the
-target :class:`_schema.Column`, meaning it will be treated
-as a default generating column when INSERT statements are generated,
-as well as that it will be fetched as a generating column when using the ORM.
-This includes that it will be part of the RETURNING clause of the database
-for databases which support RETURNING and the generated values are to be
-eagerly fetched.
-
-The :class:`.Identity` construct is currently known to be supported by:
-
-* PostgreSQL as of version 10.
-
-* Oracle as of version 12. It also supports passing ``always=None`` to
- enable the default generated mode and the parameter ``on_null=True`` to
- specify "ON NULL" in conjunction with a "BY DEFAULT" identity column.
-
-* Microsoft SQL Server. MSSQL uses a custom syntax that only supports the
- ``start`` and ``increment`` parameters, and ignores all other.
-
-When :class:`.Identity` is used with an unsupported backend, it is ignored,
-and the default SQLAlchemy logic for autoincrementing columns is used.
-
-An error is raised when a :class:`_schema.Column` specifies both an
-:class:`.Identity` and also sets :paramref:`_schema.Column.autoincrement`
-to ``False``.
-
-.. seealso::
-
- :class:`.Identity`
-
-
-Default Objects API
--------------------
-
-.. autoclass:: Computed
- :members:
-
-
-.. autoclass:: ColumnDefault
-
-
-.. autoclass:: DefaultClause
-
-
-.. autoclass:: DefaultGenerator
-
-
-.. autoclass:: FetchedValue
-
-
-.. autoclass:: Sequence
- :members:
-
-
-.. autoclass:: Identity
- :members:
operators
selectable
dml
- functions
compiler
- serializer
foundation
- visitors
+++ /dev/null
-.. _functions_toplevel:
-.. _generic_functions:
-
-=========================
-SQL and Generic Functions
-=========================
-
-.. currentmodule:: sqlalchemy.sql.functions
-
-SQL functions are invoked by using the :data:`_sql.func` namespace.
-See the tutorial at :ref:`tutorial_functions` for background on how to
-use the :data:`_sql.func` object to render SQL functions in statements.
-
-.. seealso::
-
- :ref:`tutorial_functions` - in the :ref:`unified_tutorial`
-
-Function API
-------------
-
-The base API for SQL functions, which provides for the :data:`_sql.func`
-namespace as well as classes that may be used for extensibility.
-
-.. autoclass:: AnsiFunction
- :exclude-members: inherit_cache, __new__
-
-.. autoclass:: Function
-
-.. autoclass:: FunctionElement
- :members:
- :exclude-members: inherit_cache, __new__
-
-.. autoclass:: GenericFunction
- :exclude-members: inherit_cache, __new__
-
-.. autofunction:: register_function
-
-
-Selected "Known" Functions
---------------------------
-
-These are :class:`.GenericFunction` implementations for a selected set of
-common SQL functions that set up the expected return type for each function
-automatically. The are invoked in the same way as any other member of the
-:data:`_sql.func` namespace::
-
- select(func.count("*")).select_from(some_table)
-
-Note that any name not known to :data:`_sql.func` generates the function name
-as is - there is no restriction on what SQL functions can be called, known or
-unknown to SQLAlchemy, built-in or user defined. The section here only
-describes those functions where SQLAlchemy already knows what argument and
-return types are in use.
-
-.. autoclass:: array_agg
- :no-members:
-
-.. autoclass:: char_length
- :no-members:
-
-.. autoclass:: coalesce
- :no-members:
-
-.. autoclass:: concat
- :no-members:
-
-.. autoclass:: count
- :no-members:
-
-.. autoclass:: cube
- :no-members:
-
-.. autoclass:: cume_dist
- :no-members:
-
-.. autoclass:: current_date
- :no-members:
-
-.. autoclass:: current_time
- :no-members:
-
-.. autoclass:: current_timestamp
- :no-members:
-
-.. autoclass:: current_user
- :no-members:
-
-.. autoclass:: dense_rank
- :no-members:
-
-.. autoclass:: grouping_sets
- :no-members:
-
-.. autoclass:: localtime
- :no-members:
-
-.. autoclass:: localtimestamp
- :no-members:
-
-.. autoclass:: max
- :no-members:
-
-.. autoclass:: min
- :no-members:
-
-.. autoclass:: mode
- :no-members:
-
-.. autoclass:: next_value
- :no-members:
-
-.. autoclass:: now
- :no-members:
-
-.. autoclass:: percent_rank
- :no-members:
-
-.. autoclass:: percentile_cont
- :no-members:
-
-.. autoclass:: percentile_disc
- :no-members:
-
-.. autoclass:: random
- :no-members:
-
-.. autoclass:: rank
- :no-members:
-
-.. autoclass:: rollup
- :no-members:
-
-.. autoclass:: session_user
- :no-members:
-
-.. autoclass:: sum
- :no-members:
-
-.. autoclass:: sysdate
- :no-members:
-
-.. autoclass:: user
- :no-members:
+++ /dev/null
-:orphan:
-
-SQLAlchemy 2.0 Future (Core)
-============================
-
-.. admonition:: We're 2.0!
-
- This page described the "future" mode provided in SQLAlchemy 1.4
- for the purposes of 1.4 -> 2.0 transition. For 2.0, the "future"
- parameter on :func:`_sa.create_engine` and :class:`_orm.Session`
- continues to remain available for backwards-compatibility support, however
- if specified must be left at the value of ``True``.
-
- .. seealso::
-
- :ref:`migration_20_toplevel` - Introduction to the 2.0 series of SQLAlchemy
-
:maxdepth: 2
expression_api
+ engines_connections
schema
types
- engines_connections
api_basics
+++ /dev/null
-.. _core_inspection_toplevel:
-.. _inspection_toplevel:
-
-Runtime Inspection API
-======================
-
-.. automodule:: sqlalchemy.inspection
-
-.. autofunction:: sqlalchemy.inspect
-
-
-Available Inspection Targets
-----------------------------
-
-Below is a listing of many of the most common inspection targets.
-
-* :class:`.Connectable` (i.e. :class:`_engine.Engine`,
- :class:`_engine.Connection`) - returns an :class:`_reflection.Inspector` object.
-* :class:`_expression.ClauseElement` - all SQL expression components, including
- :class:`_schema.Table`, :class:`_schema.Column`, serve as their own inspection objects,
- meaning any of these objects passed to :func:`_sa.inspect` return themselves.
-* ``object`` - an object given will be checked by the ORM for a mapping -
- if so, an :class:`.InstanceState` is returned representing the mapped
- state of the object. The :class:`.InstanceState` also provides access
- to per attribute state via the :class:`.AttributeState` interface as well
- as the per-flush "history" of any attribute via the :class:`.History`
- object.
-
- .. seealso::
-
- :ref:`orm_mapper_inspection_instancestate`
-
-* ``type`` (i.e. a class) - a class given will be checked by the ORM for a
- mapping - if so, a :class:`_orm.Mapper` for that class is returned.
-
- .. seealso::
-
- :ref:`orm_mapper_inspection_mapper`
-
-* mapped attribute - passing a mapped attribute to :func:`_sa.inspect`, such
- as ``inspect(MyClass.some_attribute)``, returns a :class:`.QueryableAttribute`
- object, which is the :term:`descriptor` associated with a mapped class.
- This descriptor refers to a :class:`.MapperProperty`, which is usually
- an instance of :class:`.ColumnProperty`
- or :class:`.RelationshipProperty`, via its :attr:`.QueryableAttribute.property`
- attribute.
-* :class:`.AliasedClass` - returns an :class:`.AliasedInsp` object.
-
-
+++ /dev/null
-.. currentmodule:: sqlalchemy.schema
-
-.. _metadata_reflection_toplevel:
-.. _metadata_reflection:
-
-
-Reflecting Database Objects
-===========================
-
-A :class:`~sqlalchemy.schema.Table` object can be instructed to load
-information about itself from the corresponding database schema object already
-existing within the database. This process is called *reflection*. In the
-most simple case you need only specify the table name, a :class:`~sqlalchemy.schema.MetaData`
-object, and the ``autoload_with`` argument::
-
- >>> messages = Table("messages", metadata_obj, autoload_with=engine)
- >>> [c.name for c in messages.columns]
- ['message_id', 'message_name', 'date']
-
-The above operation will use the given engine to query the database for
-information about the ``messages`` table, and will then generate
-:class:`~sqlalchemy.schema.Column`, :class:`~sqlalchemy.schema.ForeignKey`,
-and other objects corresponding to this information as though the
-:class:`~sqlalchemy.schema.Table` object were hand-constructed in Python.
-
-When tables are reflected, if a given table references another one via foreign
-key, a second :class:`~sqlalchemy.schema.Table` object is created within the
-:class:`~sqlalchemy.schema.MetaData` object representing the connection.
-Below, assume the table ``shopping_cart_items`` references a table named
-``shopping_carts``. Reflecting the ``shopping_cart_items`` table has the
-effect such that the ``shopping_carts`` table will also be loaded::
-
- >>> shopping_cart_items = Table("shopping_cart_items", metadata_obj, autoload_with=engine)
- >>> "shopping_carts" in metadata_obj.tables
- True
-
-The :class:`~sqlalchemy.schema.MetaData` has an interesting "singleton-like"
-behavior such that if you requested both tables individually,
-:class:`~sqlalchemy.schema.MetaData` will ensure that exactly one
-:class:`~sqlalchemy.schema.Table` object is created for each distinct table
-name. The :class:`~sqlalchemy.schema.Table` constructor actually returns to
-you the already-existing :class:`~sqlalchemy.schema.Table` object if one
-already exists with the given name. Such as below, we can access the already
-generated ``shopping_carts`` table just by naming it::
-
- shopping_carts = Table("shopping_carts", metadata_obj)
-
-Of course, it's a good idea to use ``autoload_with=engine`` with the above table
-regardless. This is so that the table's attributes will be loaded if they have
-not been already. The autoload operation only occurs for the table if it
-hasn't already been loaded; once loaded, new calls to
-:class:`~sqlalchemy.schema.Table` with the same name will not re-issue any
-reflection queries.
-
-.. _reflection_overriding_columns:
-
-Overriding Reflected Columns
-----------------------------
-
-Individual columns can be overridden with explicit values when reflecting
-tables; this is handy for specifying custom datatypes, constraints such as
-primary keys that may not be configured within the database, etc.::
-
- >>> mytable = Table(
- ... "mytable",
- ... metadata_obj,
- ... Column(
- ... "id", Integer, primary_key=True
- ... ), # override reflected 'id' to have primary key
- ... Column("mydata", Unicode(50)), # override reflected 'mydata' to be Unicode
- ... # additional Column objects which require no change are reflected normally
- ... autoload_with=some_engine,
- ... )
-
-.. seealso::
-
- :ref:`custom_and_decorated_types_reflection` - illustrates how the above
- column override technique applies to the use of custom datatypes with
- table reflection.
-
-Reflecting Views
-----------------
-
-The reflection system can also reflect views. Basic usage is the same as that
-of a table::
-
- my_view = Table("some_view", metadata, autoload_with=engine)
-
-Above, ``my_view`` is a :class:`~sqlalchemy.schema.Table` object with
-:class:`~sqlalchemy.schema.Column` objects representing the names and types of
-each column within the view "some_view".
-
-Usually, it's desired to have at least a primary key constraint when
-reflecting a view, if not foreign keys as well. View reflection doesn't
-extrapolate these constraints.
-
-Use the "override" technique for this, specifying explicitly those columns
-which are part of the primary key or have foreign key constraints::
-
- my_view = Table(
- "some_view",
- metadata,
- Column("view_id", Integer, primary_key=True),
- Column("related_thing", Integer, ForeignKey("othertable.thing_id")),
- autoload_with=engine,
- )
-
-Reflecting All Tables at Once
------------------------------
-
-The :class:`~sqlalchemy.schema.MetaData` object can also get a listing of
-tables and reflect the full set. This is achieved by using the
-:func:`~sqlalchemy.schema.MetaData.reflect` method. After calling it, all
-located tables are present within the :class:`~sqlalchemy.schema.MetaData`
-object's dictionary of tables::
-
- metadata_obj = MetaData()
- metadata_obj.reflect(bind=someengine)
- users_table = metadata_obj.tables["users"]
- addresses_table = metadata_obj.tables["addresses"]
-
-``metadata.reflect()`` also provides a handy way to clear or delete all the rows in a database::
-
- metadata_obj = MetaData()
- metadata_obj.reflect(bind=someengine)
- for table in reversed(metadata_obj.sorted_tables):
- someengine.execute(table.delete())
-
-.. _metadata_reflection_schemas:
-
-Reflecting Tables from Other Schemas
-------------------------------------
-
-The section :ref:`schema_table_schema_name` introduces the concept of table
-schemas, which are namespaces within a database that contain tables and other
-objects, and which can be specified explicitly. The "schema" for a
-:class:`_schema.Table` object, as well as for other objects like views, indexes and
-sequences, can be set up using the :paramref:`_schema.Table.schema` parameter,
-and also as the default schema for a :class:`_schema.MetaData` object using the
-:paramref:`_schema.MetaData.schema` parameter.
-
-The use of this schema parameter directly affects where the table reflection
-feature will look when it is asked to reflect objects. For example, given
-a :class:`_schema.MetaData` object configured with a default schema name
-"project" via its :paramref:`_schema.MetaData.schema` parameter::
-
- >>> metadata_obj = MetaData(schema="project")
-
-The :meth:`.MetaData.reflect` will then utilize that configured ``.schema``
-for reflection::
-
- >>> # uses `schema` configured in metadata_obj
- >>> metadata_obj.reflect(someengine)
-
-The end result is that :class:`_schema.Table` objects from the "project"
-schema will be reflected, and they will be populated as schema-qualified
-with that name::
-
- >>> metadata_obj.tables["project.messages"]
- Table('messages', MetaData(), Column('message_id', INTEGER(), table=<messages>), schema='project')
-
-Similarly, an individual :class:`_schema.Table` object that includes the
-:paramref:`_schema.Table.schema` parameter will also be reflected from that
-database schema, overriding any default schema that may have been configured on the
-owning :class:`_schema.MetaData` collection::
-
- >>> messages = Table("messages", metadata_obj, schema="project", autoload_with=someengine)
- >>> messages
- Table('messages', MetaData(), Column('message_id', INTEGER(), table=<messages>), schema='project')
-
-Finally, the :meth:`_schema.MetaData.reflect` method itself also allows a
-:paramref:`_schema.MetaData.reflect.schema` parameter to be passed, so we
-could also load tables from the "project" schema for a default configured
-:class:`_schema.MetaData` object::
-
- >>> metadata_obj = MetaData()
- >>> metadata_obj.reflect(someengine, schema="project")
-
-We can call :meth:`_schema.MetaData.reflect` any number of times with different
-:paramref:`_schema.MetaData.schema` arguments (or none at all) to continue
-populating the :class:`_schema.MetaData` object with more objects::
-
- >>> # add tables from the "customer" schema
- >>> metadata_obj.reflect(someengine, schema="customer")
- >>> # add tables from the default schema
- >>> metadata_obj.reflect(someengine)
-
-.. _reflection_schema_qualified_interaction:
-
-Interaction of Schema-qualified Reflection with the Default Schema
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-.. admonition:: Section Best Practices Summarized
-
- In this section, we discuss SQLAlchemy's reflection behavior regarding
- tables that are visible in the "default schema" of a database session,
- and how these interact with SQLAlchemy directives that include the schema
- explicitly. As a best practice, ensure the "default" schema for a database
- is just a single name, and not a list of names; for tables that are
- part of this "default" schema and can be named without schema qualification
- in DDL and SQL, leave corresponding :paramref:`_schema.Table.schema` and
- similar schema parameters set to their default of ``None``.
-
-As described at :ref:`schema_metadata_schema_name`, databases that have
-the concept of schemas usually also include the concept of a "default" schema.
-The reason for this is naturally that when one refers to table objects without
-a schema as is common, a schema-capable database will still consider that
-table to be in a "schema" somewhere. Some databases such as PostgreSQL
-take this concept further into the notion of a
-`schema search path
-<https://www.postgresql.org/docs/current/static/ddl-schemas.html#DDL-SCHEMAS-PATH>`_
-where *multiple* schema names can be considered in a particular database
-session to be "implicit"; referring to a table name that it's any of those
-schemas will not require that the schema name be present (while at the same time
-it's also perfectly fine if the schema name *is* present).
-
-Since most relational databases therefore have the concept of a particular
-table object which can be referred towards both in a schema-qualified way, as
-well as an "implicit" way where no schema is present, this presents a
-complexity for SQLAlchemy's reflection
-feature. Reflecting a table in
-a schema-qualified manner will always populate its :attr:`_schema.Table.schema`
-attribute and additionally affect how this :class:`_schema.Table` is organized
-into the :attr:`_schema.MetaData.tables` collection, that is, in a schema
-qualified manner. Conversely, reflecting the **same** table in a non-schema
-qualified manner will organize it into the :attr:`_schema.MetaData.tables`
-collection **without** being schema qualified. The end result is that there
-would be two separate :class:`_schema.Table` objects in the single
-:class:`_schema.MetaData` collection representing the same table in the
-actual database.
-
-To illustrate the ramifications of this issue, consider tables from the
-"project" schema in the previous example, and suppose also that the "project"
-schema is the default schema of our database connection, or if using a database
-such as PostgreSQL suppose the "project" schema is set up in the PostgreSQL
-``search_path``. This would mean that the database accepts the following
-two SQL statements as equivalent:
-
-.. sourcecode:: sql
-
- -- schema qualified
- SELECT message_id FROM project.messages
-
- -- non-schema qualified
- SELECT message_id FROM messages
-
-This is not a problem as the table can be found in both ways. However
-in SQLAlchemy, it's the **identity** of the :class:`_schema.Table` object
-that determines its semantic role within a SQL statement. Based on the current
-decisions within SQLAlchemy, this means that if we reflect the same "messages" table in
-both a schema-qualified as well as a non-schema qualified manner, we get
-**two** :class:`_schema.Table` objects that will **not** be treated as
-semantically equivalent::
-
- >>> # reflect in non-schema qualified fashion
- >>> messages_table_1 = Table("messages", metadata_obj, autoload_with=someengine)
- >>> # reflect in schema qualified fashion
- >>> messages_table_2 = Table(
- ... "messages", metadata_obj, schema="project", autoload_with=someengine
- ... )
- >>> # two different objects
- >>> messages_table_1 is messages_table_2
- False
- >>> # stored in two different ways
- >>> metadata.tables["messages"] is messages_table_1
- True
- >>> metadata.tables["project.messages"] is messages_table_2
- True
-
-The above issue becomes more complicated when the tables being reflected contain
-foreign key references to other tables. Suppose "messages" has a "project_id"
-column which refers to rows in another schema-local table "projects", meaning
-there is a :class:`_schema.ForeignKeyConstraint` object that is part of the
-definition of the "messages" table.
-
-We can find ourselves in a situation where one :class:`_schema.MetaData`
-collection may contain as many as four :class:`_schema.Table` objects
-representing these two database tables, where one or two of the additional
-tables were generated by the reflection process; this is because when
-the reflection process encounters a foreign key constraint on a table
-being reflected, it branches out to reflect that referenced table as well.
-The decision making it uses to assign the schema to this referenced
-table is that SQLAlchemy will **omit a default schema** from the reflected
-:class:`_schema.ForeignKeyConstraint` object if the owning
-:class:`_schema.Table` also omits its schema name and also that these two objects
-are in the same schema, but will **include** it if
-it were not omitted.
-
-The common scenario is when the reflection of a table in a schema qualified
-fashion then loads a related table that will also be performed in a schema
-qualified fashion::
-
- >>> # reflect "messages" in a schema qualified fashion
- >>> messages_table_1 = Table(
- ... "messages", metadata_obj, schema="project", autoload_with=someengine
- ... )
-
-The above ``messages_table_1`` will refer to ``projects`` also in a schema
-qualified fashion. This "projects" table will be reflected automatically by
-the fact that "messages" refers to it::
-
- >>> messages_table_1.c.project_id
- Column('project_id', INTEGER(), ForeignKey('project.projects.project_id'), table=<messages>)
-
-if some other part of the code reflects "projects" in a non-schema qualified
-fashion, there are now two projects tables that are not the same:
-
- >>> # reflect "projects" in a non-schema qualified fashion
- >>> projects_table_1 = Table("projects", metadata_obj, autoload_with=someengine)
-
- >>> # messages does not refer to projects_table_1 above
- >>> messages_table_1.c.project_id.references(projects_table_1.c.project_id)
- False
-
- >>> # it refers to this one
- >>> projects_table_2 = metadata_obj.tables["project.projects"]
- >>> messages_table_1.c.project_id.references(projects_table_2.c.project_id)
- True
-
- >>> # they're different, as one non-schema qualified and the other one is
- >>> projects_table_1 is projects_table_2
- False
-
-The above confusion can cause problems within applications that use table
-reflection to load up application-level :class:`_schema.Table` objects, as
-well as within migration scenarios, in particular such as when using Alembic
-Migrations to detect new tables and foreign key constraints.
-
-The above behavior can be remedied by sticking to one simple practice:
-
-* Don't include the :paramref:`_schema.Table.schema` parameter for any
- :class:`_schema.Table` that expects to be located in the **default** schema
- of the database.
-
-For PostgreSQL and other databases that support a "search" path for schemas,
-add the following additional practice:
-
-* Keep the "search path" narrowed down to **one schema only, which is the
- default schema**.
-
-
-.. seealso::
-
- :ref:`postgresql_schema_reflection` - additional details of this behavior
- as regards the PostgreSQL database.
-
-
-.. _metadata_reflection_inspector:
-
-Fine Grained Reflection with Inspector
---------------------------------------
-
-A low level interface which provides a backend-agnostic system of loading
-lists of schema, table, column, and constraint descriptions from a given
-database is also available. This is known as the "Inspector"::
-
- from sqlalchemy import create_engine
- from sqlalchemy import inspect
-
- engine = create_engine("...")
- insp = inspect(engine)
- print(insp.get_table_names())
-
-.. autoclass:: sqlalchemy.engine.reflection.Inspector
- :members:
- :undoc-members:
-
-.. autoclass:: sqlalchemy.engine.interfaces.ReflectedColumn
- :members:
- :inherited-members: dict
-
-.. autoclass:: sqlalchemy.engine.interfaces.ReflectedComputed
- :members:
- :inherited-members: dict
-
-.. autoclass:: sqlalchemy.engine.interfaces.ReflectedCheckConstraint
- :members:
- :inherited-members: dict
-
-.. autoclass:: sqlalchemy.engine.interfaces.ReflectedForeignKeyConstraint
- :members:
- :inherited-members: dict
-
-.. autoclass:: sqlalchemy.engine.interfaces.ReflectedIdentity
- :members:
- :inherited-members: dict
-
-.. autoclass:: sqlalchemy.engine.interfaces.ReflectedIndex
- :members:
- :inherited-members: dict
-
-.. autoclass:: sqlalchemy.engine.interfaces.ReflectedPrimaryKeyConstraint
- :members:
- :inherited-members: dict
-
-.. autoclass:: sqlalchemy.engine.interfaces.ReflectedUniqueConstraint
- :members:
- :inherited-members: dict
-
-.. autoclass:: sqlalchemy.engine.interfaces.ReflectedTableComment
- :members:
- :inherited-members: dict
-
-
-.. _metadata_reflection_dbagnostic_types:
-
-Reflecting with Database-Agnostic Types
----------------------------------------
-
-When the columns of a table are reflected, using either the
-:paramref:`_schema.Table.autoload_with` parameter of :class:`_schema.Table` or
-the :meth:`_reflection.Inspector.get_columns` method of
-:class:`_reflection.Inspector`, the datatypes will be as specific as possible
-to the target database. This means that if an "integer" datatype is reflected
-from a MySQL database, the type will be represented by the
-:class:`sqlalchemy.dialects.mysql.INTEGER` class, which includes MySQL-specific
-attributes such as "display_width". Or on PostgreSQL, a PostgreSQL-specific
-datatype such as :class:`sqlalchemy.dialects.postgresql.INTERVAL` or
-:class:`sqlalchemy.dialects.postgresql.ENUM` may be returned.
-
-There is a use case for reflection which is that a given :class:`_schema.Table`
-is to be transferred to a different vendor database. To suit this use case,
-there is a technique by which these vendor-specific datatypes can be converted
-on the fly to be instance of SQLAlchemy backend-agnostic datatypes, for
-the examples above types such as :class:`_types.Integer`, :class:`_types.Interval`
-and :class:`_types.Enum`. This may be achieved by intercepting the
-column reflection using the :meth:`_events.DDLEvents.column_reflect` event
-in conjunction with the :meth:`_types.TypeEngine.as_generic` method.
-
-Given a table in MySQL (chosen because MySQL has a lot of vendor-specific
-datatypes and options):
-
-.. sourcecode:: sql
-
- CREATE TABLE IF NOT EXISTS my_table (
- id INTEGER PRIMARY KEY AUTO_INCREMENT,
- data1 VARCHAR(50) CHARACTER SET latin1,
- data2 MEDIUMINT(4),
- data3 TINYINT(2)
- )
-
-The above table includes MySQL-only integer types ``MEDIUMINT`` and
-``TINYINT`` as well as a ``VARCHAR`` that includes the MySQL-only ``CHARACTER
-SET`` option. If we reflect this table normally, it produces a
-:class:`_schema.Table` object that will contain those MySQL-specific datatypes
-and options:
-
-.. sourcecode:: pycon+sql
-
- >>> from sqlalchemy import MetaData, Table, create_engine
- >>> mysql_engine = create_engine("mysql+mysqldb://scott:tiger@localhost/test")
- >>> metadata_obj = MetaData()
- >>> my_mysql_table = Table("my_table", metadata_obj, autoload_with=mysql_engine)
-
-The above example reflects the above table schema into a new :class:`_schema.Table`
-object. We can then, for demonstration purposes, print out the MySQL-specific
-"CREATE TABLE" statement using the :class:`_schema.CreateTable` construct:
-
-.. sourcecode:: pycon+sql
-
- >>> from sqlalchemy.schema import CreateTable
- >>> print(CreateTable(my_mysql_table).compile(mysql_engine))
- {printsql}CREATE TABLE my_table (
- id INTEGER(11) NOT NULL AUTO_INCREMENT,
- data1 VARCHAR(50) CHARACTER SET latin1,
- data2 MEDIUMINT(4),
- data3 TINYINT(2),
- PRIMARY KEY (id)
- )ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
-
-
-Above, the MySQL-specific datatypes and options were maintained. If we wanted
-a :class:`_schema.Table` that we could instead transfer cleanly to another
-database vendor, replacing the special datatypes
-:class:`sqlalchemy.dialects.mysql.MEDIUMINT` and
-:class:`sqlalchemy.dialects.mysql.TINYINT` with :class:`_types.Integer`, we can
-choose instead to "genericize" the datatypes on this table, or otherwise change
-them in any way we'd like, by establishing a handler using the
-:meth:`_events.DDLEvents.column_reflect` event. The custom handler will make use
-of the :meth:`_types.TypeEngine.as_generic` method to convert the above
-MySQL-specific type objects into generic ones, by replacing the ``"type"``
-entry within the column dictionary entry that is passed to the event handler.
-The format of this dictionary is described at :meth:`_reflection.Inspector.get_columns`:
-
-.. sourcecode:: pycon+sql
-
- >>> from sqlalchemy import event
- >>> metadata_obj = MetaData()
-
- >>> @event.listens_for(metadata_obj, "column_reflect")
- ... def genericize_datatypes(inspector, tablename, column_dict):
- ... column_dict["type"] = column_dict["type"].as_generic()
-
- >>> my_generic_table = Table("my_table", metadata_obj, autoload_with=mysql_engine)
-
-We now get a new :class:`_schema.Table` that is generic and uses
-:class:`_types.Integer` for those datatypes. We can now emit a
-"CREATE TABLE" statement for example on a PostgreSQL database:
-
-.. sourcecode:: pycon+sql
-
- >>> pg_engine = create_engine("postgresql+psycopg2://scott:tiger@localhost/test", echo=True)
- >>> my_generic_table.create(pg_engine)
- {execsql}CREATE TABLE my_table (
- id SERIAL NOT NULL,
- data1 VARCHAR(50),
- data2 INTEGER,
- data3 INTEGER,
- PRIMARY KEY (id)
- )
-
-Noting above also that SQLAlchemy will usually make a decent guess for other
-behaviors, such as that the MySQL ``AUTO_INCREMENT`` directive is represented
-in PostgreSQL most closely using the ``SERIAL`` auto-incrementing datatype.
-
-.. versionadded:: 1.4 Added the :meth:`_types.TypeEngine.as_generic` method
- and additionally improved the use of the :meth:`_events.DDLEvents.column_reflect`
- event such that it may be applied to a :class:`_schema.MetaData` object
- for convenience.
-
-
-Limitations of Reflection
--------------------------
-
-It's important to note that the reflection process recreates :class:`_schema.Table`
-metadata using only information which is represented in the relational database.
-This process by definition cannot restore aspects of a schema that aren't
-actually stored in the database. State which is not available from reflection
-includes but is not limited to:
-
-* Client side defaults, either Python functions or SQL expressions defined using
- the ``default`` keyword of :class:`_schema.Column` (note this is separate from ``server_default``,
- which specifically is what's available via reflection).
-
-* Column information, e.g. data that might have been placed into the
- :attr:`_schema.Column.info` dictionary
-
-* The value of the ``.quote`` setting for :class:`_schema.Column` or :class:`_schema.Table`
-
-* The association of a particular :class:`.Sequence` with a given :class:`_schema.Column`
-
-The relational database also in many cases reports on table metadata in a
-different format than what was specified in SQLAlchemy. The :class:`_schema.Table`
-objects returned from reflection cannot be always relied upon to produce the identical
-DDL as the original Python-defined :class:`_schema.Table` objects. Areas where
-this occurs includes server defaults, column-associated sequences and various
-idiosyncrasies regarding constraints and datatypes. Server side defaults may
-be returned with cast directives (typically PostgreSQL will include a ``::<type>``
-cast) or different quoting patterns than originally specified.
-
-Another category of limitation includes schema structures for which reflection
-is only partially or not yet defined. Recent improvements to reflection allow
-things like views, indexes and foreign key options to be reflected. As of this
-writing, structures like CHECK constraints, table comments, and triggers are
-not reflected.
-
:maxdepth: 3
metadata
- reflection
- defaults
constraints
ddl
+++ /dev/null
-Expression Serializer Extension
-===============================
-
-.. automodule:: sqlalchemy.ext.serializer
- :members:
- :undoc-members:
+++ /dev/null
-Visitor and Traversal Utilities
-================================
-
-The :mod:`sqlalchemy.sql.visitors` module consists of classes and functions
-that serve the purpose of generically **traversing** a Core SQL expression
-structure. This is not unlike the Python ``ast`` module in that is presents
-a system by which a program can operate upon each component of a SQL
-expression. Common purposes this serves are locating various kinds of
-elements such as :class:`_schema.Table` or :class:`.BindParameter` objects,
-as well as altering the state of the structure such as replacing certain FROM
-clauses with others.
-
-.. note:: the :mod:`sqlalchemy.sql.visitors` module is an internal API and
- is not fully public. It is subject to change and may additionally not
- function as expected for use patterns that aren't considered within
- SQLAlchemy's own internals.
-
-The :mod:`sqlalchemy.sql.visitors` module is part of the **internals** of
-SQLAlchemy and it is not usually used by calling application code. It is
-however used in certain edge cases such as when constructing caching routines
-as well as when building out custom SQL expressions using the
-:ref:`Custom SQL Constructs and Compilation Extension <sqlalchemy.ext.compiler_toplevel>`.
-
-.. automodule:: sqlalchemy.sql.visitors
- :members:
- :private-members:
-
:glob:
postgresql
- mysql
- sqlite
- oracle
- mssql
-Support Levels for Included Dialects
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The following table summarizes the support level for each included dialect.
-
-.. dialect-table:: **Supported database versions for included dialects**
- :header-rows: 1
Support Definitions
^^^^^^^^^^^^^^^^^^^
+++ /dev/null
-.. _mssql_toplevel:
-
-Microsoft SQL Server
-====================
-
-.. automodule:: sqlalchemy.dialects.mssql.base
-
-SQL Server SQL Constructs
--------------------------
-
-.. currentmodule:: sqlalchemy.dialects.mssql
-
-.. autofunction:: try_cast
-
-SQL Server Data Types
----------------------
-
-As with all SQLAlchemy dialects, all UPPERCASE types that are known to be
-valid with SQL server are importable from the top level dialect, whether
-they originate from :mod:`sqlalchemy.types` or from the local dialect::
-
- from sqlalchemy.dialects.mssql import (
- BIGINT,
- BINARY,
- BIT,
- CHAR,
- DATE,
- DATETIME,
- DATETIME2,
- DATETIMEOFFSET,
- DECIMAL,
- FLOAT,
- IMAGE,
- INTEGER,
- JSON,
- MONEY,
- NCHAR,
- NTEXT,
- NUMERIC,
- NVARCHAR,
- REAL,
- SMALLDATETIME,
- SMALLINT,
- SMALLMONEY,
- SQL_VARIANT,
- TEXT,
- TIME,
- TIMESTAMP,
- TINYINT,
- UNIQUEIDENTIFIER,
- VARBINARY,
- VARCHAR,
- )
-
-Types which are specific to SQL Server, or have SQL Server-specific
-construction arguments, are as follows:
-
-.. note: where :noindex: is used, indicates a type that is not redefined
- in the dialect module, just imported from sqltypes. this avoids warnings
- in the sphinx build
-
-.. currentmodule:: sqlalchemy.dialects.mssql
-
-.. autoclass:: BIT
- :members: __init__
-
-
-.. autoclass:: CHAR
- :members: __init__
- :noindex:
-
-
-.. autoclass:: DATETIME2
- :members: __init__
-
-
-.. autoclass:: DATETIMEOFFSET
- :members: __init__
-
-
-.. autoclass:: IMAGE
- :members: __init__
-
-
-.. autoclass:: JSON
- :members: __init__
-
-
-.. autoclass:: MONEY
- :members: __init__
-
-
-.. autoclass:: NCHAR
- :members: __init__
- :noindex:
-
-
-.. autoclass:: NTEXT
- :members: __init__
-
-
-.. autoclass:: NVARCHAR
- :members: __init__
- :noindex:
-
-.. autoclass:: REAL
- :members: __init__
-
-.. autoclass:: ROWVERSION
- :members: __init__
-
-.. autoclass:: SMALLDATETIME
- :members: __init__
-
-
-.. autoclass:: SMALLMONEY
- :members: __init__
-
-
-.. autoclass:: SQL_VARIANT
- :members: __init__
-
-
-.. autoclass:: TEXT
- :members: __init__
- :noindex:
-
-.. autoclass:: TIME
- :members: __init__
-
-
-.. autoclass:: TIMESTAMP
- :members: __init__
-
-.. autoclass:: TINYINT
- :members: __init__
-
-
-.. autoclass:: UNIQUEIDENTIFIER
- :members: __init__
-
-
-.. autoclass:: VARBINARY
- :members: __init__
- :noindex:
-
-.. autoclass:: VARCHAR
- :members: __init__
- :noindex:
-
-
-.. autoclass:: XML
- :members: __init__
-
-
-PyODBC
-------
-.. automodule:: sqlalchemy.dialects.mssql.pyodbc
-
-pymssql
--------
-.. automodule:: sqlalchemy.dialects.mssql.pymssql
-
+++ /dev/null
-.. _mysql_toplevel:
-
-MySQL and MariaDB
-=================
-
-.. automodule:: sqlalchemy.dialects.mysql.base
-
-MySQL SQL Constructs
---------------------
-
-.. currentmodule:: sqlalchemy.dialects.mysql
-
-.. autoclass:: match
- :members:
-
-MySQL Data Types
-----------------
-
-As with all SQLAlchemy dialects, all UPPERCASE types that are known to be
-valid with MySQL are importable from the top level dialect::
-
- from sqlalchemy.dialects.mysql import (
- BIGINT,
- BINARY,
- BIT,
- BLOB,
- BOOLEAN,
- CHAR,
- DATE,
- DATETIME,
- DECIMAL,
- DECIMAL,
- DOUBLE,
- ENUM,
- FLOAT,
- INTEGER,
- LONGBLOB,
- LONGTEXT,
- MEDIUMBLOB,
- MEDIUMINT,
- MEDIUMTEXT,
- NCHAR,
- NUMERIC,
- NVARCHAR,
- REAL,
- SET,
- SMALLINT,
- TEXT,
- TIME,
- TIMESTAMP,
- TINYBLOB,
- TINYINT,
- TINYTEXT,
- VARBINARY,
- VARCHAR,
- YEAR,
- )
-
-Types which are specific to MySQL, or have MySQL-specific
-construction arguments, are as follows:
-
-.. note: where :noindex: is used, indicates a type that is not redefined
- in the dialect module, just imported from sqltypes. this avoids warnings
- in the sphinx build
-
-.. currentmodule:: sqlalchemy.dialects.mysql
-
-.. autoclass:: BIGINT
- :members: __init__
-
-
-.. autoclass:: BINARY
- :noindex:
- :members: __init__
-
-
-.. autoclass:: BIT
- :members: __init__
-
-
-.. autoclass:: BLOB
- :members: __init__
- :noindex:
-
-
-.. autoclass:: BOOLEAN
- :members: __init__
- :noindex:
-
-
-.. autoclass:: CHAR
- :members: __init__
-
-
-.. autoclass:: DATE
- :members: __init__
- :noindex:
-
-
-.. autoclass:: DATETIME
- :members: __init__
-
-
-.. autoclass:: DECIMAL
- :members: __init__
-
-
-.. autoclass:: DOUBLE
- :members: __init__
- :noindex:
-
-.. autoclass:: ENUM
- :members: __init__
-
-
-.. autoclass:: FLOAT
- :members: __init__
-
-
-.. autoclass:: INTEGER
- :members: __init__
-
-.. autoclass:: JSON
- :members:
-
-.. autoclass:: LONGBLOB
- :members: __init__
-
-
-.. autoclass:: LONGTEXT
- :members: __init__
-
-
-.. autoclass:: MEDIUMBLOB
- :members: __init__
-
-
-.. autoclass:: MEDIUMINT
- :members: __init__
-
-
-.. autoclass:: MEDIUMTEXT
- :members: __init__
-
-
-.. autoclass:: NCHAR
- :members: __init__
-
-
-.. autoclass:: NUMERIC
- :members: __init__
-
-
-.. autoclass:: NVARCHAR
- :members: __init__
-
-
-.. autoclass:: REAL
- :members: __init__
-
-
-.. autoclass:: SET
- :members: __init__
-
-
-.. autoclass:: SMALLINT
- :members: __init__
-
-
-.. autoclass:: TEXT
- :members: __init__
- :noindex:
-
-
-.. autoclass:: TIME
- :members: __init__
-
-
-.. autoclass:: TIMESTAMP
- :members: __init__
-
-
-.. autoclass:: TINYBLOB
- :members: __init__
-
-
-.. autoclass:: TINYINT
- :members: __init__
-
-
-.. autoclass:: TINYTEXT
- :members: __init__
-
-
-.. autoclass:: VARBINARY
- :members: __init__
- :noindex:
-
-
-.. autoclass:: VARCHAR
- :members: __init__
-
-
-.. autoclass:: YEAR
- :members: __init__
-
-MySQL DML Constructs
--------------------------
-
-.. autofunction:: sqlalchemy.dialects.mysql.insert
-
-.. autoclass:: sqlalchemy.dialects.mysql.Insert
- :members:
-
-
-
-mysqlclient (fork of MySQL-Python)
-----------------------------------
-
-.. automodule:: sqlalchemy.dialects.mysql.mysqldb
-
-PyMySQL
--------
-
-.. automodule:: sqlalchemy.dialects.mysql.pymysql
-
-MariaDB-Connector
-------------------
-
-.. automodule:: sqlalchemy.dialects.mysql.mariadbconnector
-
-MySQL-Connector
----------------
-
-.. automodule:: sqlalchemy.dialects.mysql.mysqlconnector
-
-.. _asyncmy:
-
-asyncmy
--------
-
-.. automodule:: sqlalchemy.dialects.mysql.asyncmy
-
-
-.. _aiomysql:
-
-aiomysql
---------
-
-.. automodule:: sqlalchemy.dialects.mysql.aiomysql
-
-cymysql
--------
-
-.. automodule:: sqlalchemy.dialects.mysql.cymysql
-
-pyodbc
-------
-
-.. automodule:: sqlalchemy.dialects.mysql.pyodbc
+++ /dev/null
-.. _oracle_toplevel:
-
-Oracle
-======
-
-.. automodule:: sqlalchemy.dialects.oracle.base
-
-Oracle Data Types
------------------
-
-As with all SQLAlchemy dialects, all UPPERCASE types that are known to be
-valid with Oracle are importable from the top level dialect, whether
-they originate from :mod:`sqlalchemy.types` or from the local dialect::
-
- from sqlalchemy.dialects.oracle import (
- BFILE,
- BLOB,
- CHAR,
- CLOB,
- DATE,
- DOUBLE_PRECISION,
- FLOAT,
- INTERVAL,
- LONG,
- NCLOB,
- NCHAR,
- NUMBER,
- NVARCHAR,
- NVARCHAR2,
- RAW,
- TIMESTAMP,
- VARCHAR,
- VARCHAR2,
- )
-
-.. versionadded:: 1.2.19 Added :class:`_types.NCHAR` to the list of datatypes
- exported by the Oracle dialect.
-
-Types which are specific to Oracle, or have Oracle-specific
-construction arguments, are as follows:
-
-.. currentmodule:: sqlalchemy.dialects.oracle
-
-.. autoclass:: BFILE
- :members: __init__
-
-.. autoclass:: DATE
- :members: __init__
-
-.. autoclass:: FLOAT
- :members: __init__
-
-.. autoclass:: INTERVAL
- :members: __init__
-
-.. autoclass:: NCLOB
- :members: __init__
-
-.. autoclass:: NUMBER
- :members: __init__
-
-.. autoclass:: LONG
- :members: __init__
-
-.. autoclass:: RAW
- :members: __init__
-
-.. autoclass:: TIMESTAMP
- :members: __init__
-
-.. _cx_oracle:
-
-cx_Oracle
----------
-
-.. automodule:: sqlalchemy.dialects.oracle.cx_oracle
-
-.. _oracledb:
-
-python-oracledb
----------------
-
-.. automodule:: sqlalchemy.dialects.oracle.oracledb
-
PostgreSQL
==========
-.. automodule:: sqlalchemy.dialects.postgresql.base
+The PG module
+
+.. .. automodule:: sqlalchemy.dialects.postgresql.base
ARRAY Types
-----------
.. _postgresql_psycopg2:
-psycopg2
---------
-
-.. automodule:: sqlalchemy.dialects.postgresql.psycopg2
-
-.. _postgresql_psycopg:
-
-psycopg
---------
-
-.. automodule:: sqlalchemy.dialects.postgresql.psycopg
-
-pg8000
-------
-
-.. automodule:: sqlalchemy.dialects.postgresql.pg8000
-
-.. _dialect-postgresql-asyncpg:
-
-asyncpg
--------
-
-.. automodule:: sqlalchemy.dialects.postgresql.asyncpg
-
-psycopg2cffi
-------------
-
-.. automodule:: sqlalchemy.dialects.postgresql.psycopg2cffi
+++ /dev/null
-.. _sqlite_toplevel:
-
-SQLite
-======
-
-.. automodule:: sqlalchemy.dialects.sqlite.base
-
-SQLite Data Types
------------------
-
-As with all SQLAlchemy dialects, all UPPERCASE types that are known to be
-valid with SQLite are importable from the top level dialect, whether
-they originate from :mod:`sqlalchemy.types` or from the local dialect::
-
- from sqlalchemy.dialects.sqlite import (
- BLOB,
- BOOLEAN,
- CHAR,
- DATE,
- DATETIME,
- DECIMAL,
- FLOAT,
- INTEGER,
- NUMERIC,
- JSON,
- SMALLINT,
- TEXT,
- TIME,
- TIMESTAMP,
- VARCHAR,
- )
-
-.. module:: sqlalchemy.dialects.sqlite
-
-.. autoclass:: DATETIME
-
-.. autoclass:: DATE
-
-.. autoclass:: JSON
-
-.. autoclass:: TIME
-
-SQLite DML Constructs
--------------------------
-
-.. autofunction:: sqlalchemy.dialects.sqlite.insert
-
-.. autoclass:: sqlalchemy.dialects.sqlite.Insert
- :members:
-
-.. _pysqlite:
-
-Pysqlite
---------
-
-.. automodule:: sqlalchemy.dialects.sqlite.pysqlite
-
-.. _aiosqlite:
-
-Aiosqlite
----------
-
-.. automodule:: sqlalchemy.dialects.sqlite.aiosqlite
-
-
-.. _pysqlcipher:
-
-Pysqlcipher
------------
-
-.. automodule:: sqlalchemy.dialects.sqlite.pysqlcipher
+++ /dev/null
-.. _relationships_backref:
-
-Using the legacy 'backref' relationship parameter
---------------------------------------------------
-
-.. note:: The :paramref:`_orm.relationship.backref` keyword should be considered
- legacy, and use of :paramref:`_orm.relationship.back_populates` with explicit
- :func:`_orm.relationship` constructs should be preferred. Using
- individual :func:`_orm.relationship` constructs provides advantages
- including that both ORM mapped classes will include their attributes
- up front as the class is constructed, rather than as a deferred step,
- and configuration is more straightforward as all arguments are explicit.
- New :pep:`484` features in SQLAlchemy 2.0 also take advantage of
- attributes being explicitly present in source code rather than
- using dynamic attribute generation.
-
-.. seealso::
-
- For general information about bidirectional relationships, see the
- following sections:
-
- :ref:`tutorial_orm_related_objects` - in the :ref:`unified_tutorial`,
- presents an overview of bi-directional relationship configuration
- and behaviors using :paramref:`_orm.relationship.back_populates`
-
- :ref:`back_populates_cascade` - notes on bi-directional :func:`_orm.relationship`
- behavior regarding :class:`_orm.Session` cascade behaviors.
-
- :paramref:`_orm.relationship.back_populates`
-
-
-The :paramref:`_orm.relationship.backref` keyword argument on the
-:func:`_orm.relationship` construct allows the
-automatic generation of a new :func:`_orm.relationship` that will be automatically
-be added to the ORM mapping for the related class. It will then be
-placed into a :paramref:`_orm.relationship.back_populates` configuration
-against the current :func:`_orm.relationship` being configured, with both
-:func:`_orm.relationship` constructs referring to each other.
-
-Starting with the following example::
-
- from sqlalchemy import Column, ForeignKey, Integer, String
- from sqlalchemy.orm import DeclarativeBase, relationship
-
-
- class Base(DeclarativeBase):
- pass
-
-
- class User(Base):
- __tablename__ = "user"
- id = mapped_column(Integer, primary_key=True)
- name = mapped_column(String)
-
- addresses = relationship("Address", backref="user")
-
-
- class Address(Base):
- __tablename__ = "address"
- id = mapped_column(Integer, primary_key=True)
- email = mapped_column(String)
- user_id = mapped_column(Integer, ForeignKey("user.id"))
-
-The above configuration establishes a collection of ``Address`` objects on ``User`` called
-``User.addresses``. It also establishes a ``.user`` attribute on ``Address`` which will
-refer to the parent ``User`` object. Using :paramref:`_orm.relationship.back_populates`
-it's equivalent to the following::
-
- from sqlalchemy import Column, ForeignKey, Integer, String
- from sqlalchemy.orm import DeclarativeBase, relationship
-
-
- class Base(DeclarativeBase):
- pass
-
-
- class User(Base):
- __tablename__ = "user"
- id = mapped_column(Integer, primary_key=True)
- name = mapped_column(String)
-
- addresses = relationship("Address", back_populates="user")
-
-
- class Address(Base):
- __tablename__ = "address"
- id = mapped_column(Integer, primary_key=True)
- email = mapped_column(String)
- user_id = mapped_column(Integer, ForeignKey("user.id"))
-
- user = relationship("User", back_populates="addresses")
-
-The behavior of the ``User.addresses`` and ``Address.user`` relationships
-is that they now behave in a **bi-directional** way, indicating that
-changes on one side of the relationship impact the other. An example
-and discussion of this behavior is in the :ref:`unified_tutorial`
-at :ref:`tutorial_orm_related_objects`.
-
-
-Backref Default Arguments
-~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Since :paramref:`_orm.relationship.backref` generates a whole new
-:func:`_orm.relationship`, the generation process by default
-will attempt to include corresponding arguments in the new
-:func:`_orm.relationship` that correspond to the original arguments.
-As an example, below is a :func:`_orm.relationship` that includes a
-:ref:`custom join condition <relationship_configure_joins>`
-which also includes the :paramref:`_orm.relationship.backref` keyword::
-
- from sqlalchemy import Column, ForeignKey, Integer, String
- from sqlalchemy.orm import DeclarativeBase, relationship
-
-
- class Base(DeclarativeBase):
- pass
-
-
- class User(Base):
- __tablename__ = "user"
- id = mapped_column(Integer, primary_key=True)
- name = mapped_column(String)
-
- addresses = relationship(
- "Address",
- primaryjoin=(
- "and_(User.id==Address.user_id, Address.email.startswith('tony'))"
- ),
- backref="user",
- )
-
-
- class Address(Base):
- __tablename__ = "address"
- id = mapped_column(Integer, primary_key=True)
- email = mapped_column(String)
- user_id = mapped_column(Integer, ForeignKey("user.id"))
-
-When the "backref" is generated, the :paramref:`_orm.relationship.primaryjoin`
-condition is copied to the new :func:`_orm.relationship` as well::
-
- >>> print(User.addresses.property.primaryjoin)
- "user".id = address.user_id AND address.email LIKE :email_1 || '%%'
- >>>
- >>> print(Address.user.property.primaryjoin)
- "user".id = address.user_id AND address.email LIKE :email_1 || '%%'
- >>>
-
-Other arguments that are transferrable include the
-:paramref:`_orm.relationship.secondary` parameter that refers to a
-many-to-many association table, as well as the "join" arguments
-:paramref:`_orm.relationship.primaryjoin` and
-:paramref:`_orm.relationship.secondaryjoin`; "backref" is smart enough to know
-that these two arguments should also be "reversed" when generating
-the opposite side.
-
-Specifying Backref Arguments
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Lots of other arguments for a "backref" are not implicit, and
-include arguments like
-:paramref:`_orm.relationship.lazy`,
-:paramref:`_orm.relationship.remote_side`,
-:paramref:`_orm.relationship.cascade` and
-:paramref:`_orm.relationship.cascade_backrefs`. For this case we use
-the :func:`.backref` function in place of a string; this will store
-a specific set of arguments that will be transferred to the new
-:func:`_orm.relationship` when generated::
-
- # <other imports>
- from sqlalchemy.orm import backref
-
-
- class User(Base):
- __tablename__ = "user"
- id = mapped_column(Integer, primary_key=True)
- name = mapped_column(String)
-
- addresses = relationship(
- "Address",
- backref=backref("user", lazy="joined"),
- )
-
-Where above, we placed a ``lazy="joined"`` directive only on the ``Address.user``
-side, indicating that when a query against ``Address`` is made, a join to the ``User``
-entity should be made automatically which will populate the ``.user`` attribute of each
-returned ``Address``. The :func:`.backref` function formatted the arguments we gave
-it into a form that is interpreted by the receiving :func:`_orm.relationship` as additional
-arguments to be applied to the new relationship it creates.
-
+++ /dev/null
-:orphan:
-
-Moved! :ref:`orm_imperative_mapping`
-
-
+++ /dev/null
-:orphan:
-
-=======================================
-Collection Configuration and Techniques
-=======================================
-
-This page has been broken into two separate pages:
-
-:doc:`large_collections`
-
-:doc:`collection_api`
-
+++ /dev/null
-.. currentmodule:: sqlalchemy.orm
-
-.. _mapper_composite:
-
-Composite Column Types
-======================
-
-Sets of columns can be associated with a single user-defined datatype,
-which in modern use is normally a Python dataclass_. The ORM
-provides a single attribute which represents the group of columns using the
-class you provide.
-
-A simple example represents pairs of :class:`_types.Integer` columns as a
-``Point`` object, with attributes ``.x`` and ``.y``. Using a
-dataclass, these attributes are defined with the corresponding ``int``
-Python type::
-
- import dataclasses
-
-
- @dataclasses.dataclass
- class Point:
- x: int
- y: int
-
-Non-dataclass forms are also accepted, but require additional methods
-to be implemented. For an example using a non-dataclass class, see the section
-:ref:`composite_legacy_no_dataclass`.
-
-.. versionadded:: 2.0 The :func:`_orm.composite` construct fully supports
- Python dataclasses including the ability to derive mapped column datatypes
- from the composite class.
-
-We will create a mapping to a table ``vertices``, which represents two points
-as ``x1/y1`` and ``x2/y2``. The ``Point`` class is associated with
-the mapped columns using the :func:`_orm.composite` construct.
-
-The example below illustrates the most modern form of :func:`_orm.composite` as
-used with a fully
-:ref:`Annotated Declarative Table <orm_declarative_mapped_column>`
-configuration. :func:`_orm.mapped_column` constructs representing each column
-are passed directly to :func:`_orm.composite`, indicating zero or more aspects
-of the columns to be generated, in this case the names; the
-:func:`_orm.composite` construct derives the column types (in this case
-``int``, corresponding to :class:`_types.Integer`) from the dataclass directly::
-
- from sqlalchemy.orm import DeclarativeBase, Mapped
- from sqlalchemy.orm import composite, mapped_column
-
-
- class Base(DeclarativeBase):
- pass
-
-
- class Vertex(Base):
- __tablename__ = "vertices"
-
- id: Mapped[int] = mapped_column(primary_key=True)
-
- start: Mapped[Point] = composite(mapped_column("x1"), mapped_column("y1"))
- end: Mapped[Point] = composite(mapped_column("x2"), mapped_column("y2"))
-
- def __repr__(self):
- return f"Vertex(start={self.start}, end={self.end})"
-
-The above mapping would correspond to a CREATE TABLE statement as:
-
-.. sourcecode:: pycon+sql
-
- >>> from sqlalchemy.schema import CreateTable
- >>> print(CreateTable(Vertex.__table__))
- {printsql}CREATE TABLE vertices (
- id INTEGER NOT NULL,
- x1 INTEGER NOT NULL,
- y1 INTEGER NOT NULL,
- x2 INTEGER NOT NULL,
- y2 INTEGER NOT NULL,
- PRIMARY KEY (id)
- )
-
-
-Working with Mapped Composite Column Types
--------------------------------------------
-
-With a mapping as illustrated in the top section, we can work with the
-``Vertex`` class, where the ``.start`` and ``.end`` attributes will
-transparently refer to the columns referred towards by the ``Point`` class, as
-well as with instances of the ``Vertex`` class, where the ``.start`` and
-``.end`` attributes will refer to instances of the ``Point`` class. The ``x1``,
-``y1``, ``x2``, and ``y2`` columns are handled transparently:
-
-* **Persisting Point objects**
-
- We can create a ``Vertex`` object, assign ``Point`` objects as members,
- and they will be persisted as expected:
-
- .. sourcecode:: pycon+sql
-
- >>> v = Vertex(start=Point(3, 4), end=Point(5, 6))
- >>> session.add(v)
- >>> session.commit()
- {execsql}BEGIN (implicit)
- INSERT INTO vertices (x1, y1, x2, y2) VALUES (?, ?, ?, ?)
- [generated in ...] (3, 4, 5, 6)
- COMMIT
-
-* **Selecting Point objects as columns**
-
- :func:`_orm.composite` will allow the ``Vertex.start`` and ``Vertex.end``
- attributes to behave like a single SQL expression to as much an extent
- as possible when using the ORM :class:`_orm.Session` (including the legacy
- :class:`_orm.Query` object) to select ``Point`` objects:
-
- .. sourcecode:: pycon+sql
-
- >>> stmt = select(Vertex.start, Vertex.end)
- >>> session.execute(stmt).all()
- {execsql}SELECT vertices.x1, vertices.y1, vertices.x2, vertices.y2
- FROM vertices
- [...] ()
- {stop}[(Point(x=3, y=4), Point(x=5, y=6))]
-
-* **Comparing Point objects in SQL expressions**
-
- The ``Vertex.start`` and ``Vertex.end`` attributes may be used in
- WHERE criteria and similar, using ad-hoc ``Point`` objects for comparisons:
-
- .. sourcecode:: pycon+sql
-
- >>> stmt = select(Vertex).where(Vertex.start == Point(3, 4)).where(Vertex.end < Point(7, 8))
- >>> session.scalars(stmt).all()
- {execsql}SELECT vertices.id, vertices.x1, vertices.y1, vertices.x2, vertices.y2
- FROM vertices
- WHERE vertices.x1 = ? AND vertices.y1 = ? AND vertices.x2 < ? AND vertices.y2 < ?
- [...] (3, 4, 7, 8)
- {stop}[Vertex(Point(x=3, y=4), Point(x=5, y=6))]
-
- .. versionadded:: 2.0 :func:`_orm.composite` constructs now support
- "ordering" comparisons such as ``<``, ``>=``, and similar, in addition
- to the already-present support for ``==``, ``!=``.
-
- .. tip:: The "ordering" comparison above using the "less than" operator (``<``)
- as well as the "equality" comparison using ``==``, when used to generate
- SQL expressions, are implemented by the :class:`_orm.Composite.Comparator`
- class, and don't make use of the comparison methods on the composite class
- itself, e.g. the ``__lt__()`` or ``__eq__()`` methods. From this it
- follows that the ``Point`` dataclass above also need not implement the
- dataclasses ``order=True`` parameter for the above SQL operations to work.
- The section :ref:`composite_operations` contains background on how
- to customize the comparison operations.
-
-* **Updating Point objects on Vertex Instances**
-
- By default, the ``Point`` object **must be replaced by a new object** for
- changes to be detected:
-
- .. sourcecode:: pycon+sql
-
- >>> v1 = session.scalars(select(Vertex)).one()
- {execsql}SELECT vertices.id, vertices.x1, vertices.y1, vertices.x2, vertices.y2
- FROM vertices
- [...] ()
- {stop}
-
- >>> v1.end = Point(x=10, y=14)
- >>> session.commit()
- {execsql}UPDATE vertices SET x2=?, y2=? WHERE vertices.id = ?
- [...] (10, 14, 1)
- COMMIT
-
- In order to allow in place changes on the composite object, the
- :ref:`mutable_toplevel` extension must be used. See the section
- :ref:`mutable_composites` for examples.
-
-
-
-.. _orm_composite_other_forms:
-
-Other mapping forms for composites
-----------------------------------
-
-The :func:`_orm.composite` construct may be passed the relevant columns
-using a :func:`_orm.mapped_column` construct, a :class:`_schema.Column`,
-or the string name of an existing mapped column. The following examples
-illustrate an equvalent mapping as that of the main section above.
-
-* Map columns directly, then pass to composite
-
- Here we pass the existing :func:`_orm.mapped_column` instances to the
- :func:`_orm.composite` construct, as in the non-annotated example below
- where we also pass the ``Point`` class as the first argument to
- :func:`_orm.composite`::
-
- from sqlalchemy import Integer
- from sqlalchemy.orm import mapped_column, composite
-
-
- class Vertex(Base):
- __tablename__ = "vertices"
-
- id = mapped_column(Integer, primary_key=True)
- x1 = mapped_column(Integer)
- y1 = mapped_column(Integer)
- x2 = mapped_column(Integer)
- y2 = mapped_column(Integer)
-
- start = composite(Point, x1, y1)
- end = composite(Point, x2, y2)
-
-* Map columns directly, pass attribute names to composite
-
- We can write the same example above using more annotated forms where we have
- the option to pass attribute names to :func:`_orm.composite` instead of
- full column constructs::
-
- from sqlalchemy.orm import mapped_column, composite, Mapped
-
-
- class Vertex(Base):
- __tablename__ = "vertices"
-
- id: Mapped[int] = mapped_column(primary_key=True)
- x1: Mapped[int]
- y1: Mapped[int]
- x2: Mapped[int]
- y2: Mapped[int]
-
- start: Mapped[Point] = composite("x1", "y1")
- end: Mapped[Point] = composite("x2", "y2")
-
-* Imperative mapping and imperative table
-
- When using :ref:`imperative table <orm_imperative_table_configuration>` or
- fully :ref:`imperative <orm_imperative_mapping>` mappings, we have access
- to :class:`_schema.Column` objects directly. These may be passed to
- :func:`_orm.composite` as well, as in the imperative example below::
-
- mapper_registry.map_imperatively(
- Vertex,
- vertices_table,
- properties={
- "start": composite(Point, vertices_table.c.x1, vertices_table.c.y1),
- "end": composite(Point, vertices_table.c.x2, vertices_table.c.y2),
- },
- )
-
-.. _composite_legacy_no_dataclass:
-
-Using Legacy Non-Dataclasses
-----------------------------
-
-
-If not using a dataclass, the requirements for the custom datatype class are
-that it have a constructor
-which accepts positional arguments corresponding to its column format, and
-also provides a method ``__composite_values__()`` which returns the state of
-the object as a list or tuple, in order of its column-based attributes. It
-also should supply adequate ``__eq__()`` and ``__ne__()`` methods which test
-the equality of two instances.
-
-To illustrate the equivalent ``Point`` class from the main section
-not using a dataclass::
-
- class Point:
- def __init__(self, x, y):
- self.x = x
- self.y = y
-
- def __composite_values__(self):
- return self.x, self.y
-
- def __repr__(self):
- return f"Point(x={self.x!r}, y={self.y!r})"
-
- def __eq__(self, other):
- return isinstance(other, Point) and other.x == self.x and other.y == self.y
-
- def __ne__(self, other):
- return not self.__eq__(other)
-
-Usage with :func:`_orm.composite` then proceeds where the columns to be
-associated with the ``Point`` class must also be declared with explicit
-types, using one of the forms at :ref:`orm_composite_other_forms`.
-
-
-Tracking In-Place Mutations on Composites
------------------------------------------
-
-In-place changes to an existing composite value are
-not tracked automatically. Instead, the composite class needs to provide
-events to its parent object explicitly. This task is largely automated
-via the usage of the :class:`.MutableComposite` mixin, which uses events
-to associate each user-defined composite object with all parent associations.
-Please see the example in :ref:`mutable_composites`.
-
-.. _composite_operations:
-
-Redefining Comparison Operations for Composites
------------------------------------------------
-
-The "equals" comparison operation by default produces an AND of all
-corresponding columns equated to one another. This can be changed using
-the ``comparator_factory`` argument to :func:`.composite`, where we
-specify a custom :class:`.CompositeProperty.Comparator` class
-to define existing or new operations.
-Below we illustrate the "greater than" operator, implementing
-the same expression that the base "greater than" does::
-
- import dataclasses
-
- from sqlalchemy.orm import composite
- from sqlalchemy.orm import CompositeProperty
- from sqlalchemy.orm import DeclarativeBase
- from sqlalchemy.orm import Mapped
- from sqlalchemy.orm import mapped_column
- from sqlalchemy.sql import and_
-
-
- @dataclasses.dataclass
- class Point:
- x: int
- y: int
-
-
- class PointComparator(CompositeProperty.Comparator):
- def __gt__(self, other):
- """redefine the 'greater than' operation"""
-
- return and_(
- *[
- a > b
- for a, b in zip(
- self.__clause_element__().clauses,
- dataclasses.astuple(other),
- )
- ]
- )
-
-
- class Base(DeclarativeBase):
- pass
-
-
- class Vertex(Base):
- __tablename__ = "vertices"
-
- id: Mapped[int] = mapped_column(primary_key=True)
-
- start: Mapped[Point] = composite(
- mapped_column("x1"), mapped_column("y1"), comparator_factory=PointComparator
- )
- end: Mapped[Point] = composite(
- mapped_column("x2"), mapped_column("y2"), comparator_factory=PointComparator
- )
-
-Since ``Point`` is a dataclass, we may make use of
-``dataclasses.astuple()`` to get a tuple form of ``Point`` instances.
-
-The custom comparator then returns the appropriate SQL expression:
-
-.. sourcecode:: pycon+sql
-
- >>> print(Vertex.start > Point(5, 6))
- {printsql}vertices.x1 > :x1_1 AND vertices.y1 > :y1_1
-
-
-Nesting Composites
--------------------
-
-Composite objects can be defined to work in simple nested schemes, by
-redefining behaviors within the composite class to work as desired, then
-mapping the composite class to the full length of individual columns normally.
-This requires that additional methods to move between the "nested" and
-"flat" forms are defined.
-
-Below we reorganize the ``Vertex`` class to itself be a composite object which
-refers to ``Point`` objects. ``Vertex`` and ``Point`` can be dataclasses,
-however we will add a custom construction method to ``Vertex`` that can be used
-to create new ``Vertex`` objects given four column values, which will will
-arbitrarily name ``_generate()`` and define as a classmethod so that we can
-make new ``Vertex`` objects by passing values to the ``Vertex._generate()``
-method.
-
-We will also implement the ``__composite_values__()`` method, which is a fixed
-name recognized by the :func:`_orm.composite` construct (introduced previously
-at :ref:`composite_legacy_no_dataclass`) that indicates a standard way of
-receiving the object as a flat tuple of column values, which in this case will
-supersede the usual dataclass-oriented methodology.
-
-With our custom ``_generate()`` constructor and
-``__composite_values__()`` serializer method, we can now move between
-a flat tuple of columns and ``Vertex`` objects that contain ``Point``
-instances. The ``Vertex._generate`` method is passed as the
-first argument to the :func:`_orm.composite` construct as the source of new
-``Vertex`` instances, and the ``__composite_values__()`` method will be
-used implicitly by :func:`_orm.composite`.
-
-For the purposes of the example, the ``Vertex`` composite is then mapped to a
-class called ``HasVertex``, which is where the :class:`.Table` containing the
-four source columns ultimately resides::
-
- from __future__ import annotations
-
- import dataclasses
- from typing import Any
- from typing import Tuple
-
- from sqlalchemy.orm import composite
- from sqlalchemy.orm import DeclarativeBase
- from sqlalchemy.orm import Mapped
- from sqlalchemy.orm import mapped_column
-
-
- @dataclasses.dataclass
- class Point:
- x: int
- y: int
-
-
- @dataclasses.dataclass
- class Vertex:
- start: Point
- end: Point
-
- @classmethod
- def _generate(cls, x1: int, y1: int, x2: int, y2: int) -> Vertex:
- """generate a Vertex from a row"""
- return Vertex(Point(x1, y1), Point(x2, y2))
-
- def __composite_values__(self) -> Tuple[Any, ...]:
- """generate a row from a Vertex"""
- return dataclasses.astuple(self.start) + dataclasses.astuple(self.end)
-
-
- class Base(DeclarativeBase):
- pass
-
-
- class HasVertex(Base):
- __tablename__ = "has_vertex"
- id: Mapped[int] = mapped_column(primary_key=True)
- x1: Mapped[int]
- y1: Mapped[int]
- x2: Mapped[int]
- y2: Mapped[int]
-
- vertex: Mapped[Vertex] = composite(Vertex._generate, "x1", "y1", "x2", "y2")
-
-The above mapping can then be used in terms of ``HasVertex``, ``Vertex``, and
-``Point``::
-
- hv = HasVertex(vertex=Vertex(Point(1, 2), Point(3, 4)))
-
- session.add(hv)
- session.commit()
-
- stmt = select(HasVertex).where(HasVertex.vertex == Vertex(Point(1, 2), Point(3, 4)))
-
- hv = session.scalars(stmt).first()
- print(hv.vertex.start)
- print(hv.vertex.end)
-
-.. _dataclass: https://docs.python.org/3/library/dataclasses.html
-
-Composite API
--------------
-
-.. autofunction:: composite
-
+++ /dev/null
-.. _unitofwork_contextual:
-
-Contextual/Thread-local Sessions
-================================
-
-Recall from the section :ref:`session_faq_whentocreate`, the concept of
-"session scopes" was introduced, with an emphasis on web applications
-and the practice of linking the scope of a :class:`.Session` with that
-of a web request. Most modern web frameworks include integration tools
-so that the scope of the :class:`.Session` can be managed automatically,
-and these tools should be used as they are available.
-
-SQLAlchemy includes its own helper object, which helps with the establishment
-of user-defined :class:`.Session` scopes. It is also used by third-party
-integration systems to help construct their integration schemes.
-
-The object is the :class:`.scoped_session` object, and it represents a
-**registry** of :class:`.Session` objects. If you're not familiar with the
-registry pattern, a good introduction can be found in `Patterns of Enterprise
-Architecture <https://martinfowler.com/eaaCatalog/registry.html>`_.
-
-.. warning::
-
- The :class:`.scoped_session` registry by default uses a Python
- ``threading.local()``
- in order to track :class:`_orm.Session` instances. **This is not
- necessarily compatible with all application servers**, particularly those
- which make use of greenlets or other alternative forms of concurrency
- control, which may lead to race conditions (e.g. randomly occurring
- failures) when used in moderate to high concurrency scenarios.
- Please read :ref:`unitofwork_contextual_threadlocal` and
- :ref:`session_lifespan` below to more fully understand the implications
- of using ``threading.local()`` to track :class:`_orm.Session` objects
- and consider more explicit means of scoping when using application servers
- which are not based on traditional threads.
-
-.. note::
-
- The :class:`.scoped_session` object is a very popular and useful object
- used by many SQLAlchemy applications. However, it is important to note
- that it presents **only one approach** to the issue of :class:`.Session`
- management. If you're new to SQLAlchemy, and especially if the
- term "thread-local variable" seems strange to you, we recommend that
- if possible you familiarize first with an off-the-shelf integration
- system such as `Flask-SQLAlchemy <https://pypi.org/project/Flask-SQLAlchemy/>`_
- or `zope.sqlalchemy <https://pypi.org/project/zope.sqlalchemy>`_.
-
-A :class:`.scoped_session` is constructed by calling it, passing it a
-**factory** which can create new :class:`.Session` objects. A factory
-is just something that produces a new object when called, and in the
-case of :class:`.Session`, the most common factory is the :class:`.sessionmaker`,
-introduced earlier in this section. Below we illustrate this usage::
-
- >>> from sqlalchemy.orm import scoped_session
- >>> from sqlalchemy.orm import sessionmaker
-
- >>> session_factory = sessionmaker(bind=some_engine)
- >>> Session = scoped_session(session_factory)
-
-The :class:`.scoped_session` object we've created will now call upon the
-:class:`.sessionmaker` when we "call" the registry::
-
- >>> some_session = Session()
-
-Above, ``some_session`` is an instance of :class:`.Session`, which we
-can now use to talk to the database. This same :class:`.Session` is also
-present within the :class:`.scoped_session` registry we've created. If
-we call upon the registry a second time, we get back the **same** :class:`.Session`::
-
- >>> some_other_session = Session()
- >>> some_session is some_other_session
- True
-
-This pattern allows disparate sections of the application to call upon a global
-:class:`.scoped_session`, so that all those areas may share the same session
-without the need to pass it explicitly. The :class:`.Session` we've established
-in our registry will remain, until we explicitly tell our registry to dispose of it,
-by calling :meth:`.scoped_session.remove`::
-
- >>> Session.remove()
-
-The :meth:`.scoped_session.remove` method first calls :meth:`.Session.close` on
-the current :class:`.Session`, which has the effect of releasing any connection/transactional
-resources owned by the :class:`.Session` first, then discarding the :class:`.Session`
-itself. "Releasing" here means that connections are returned to their connection pool and any transactional state is rolled back, ultimately using the ``rollback()`` method of the underlying DBAPI connection.
-
-At this point, the :class:`.scoped_session` object is "empty", and will create
-a **new** :class:`.Session` when called again. As illustrated below, this
-is not the same :class:`.Session` we had before::
-
- >>> new_session = Session()
- >>> new_session is some_session
- False
-
-The above series of steps illustrates the idea of the "registry" pattern in a
-nutshell. With that basic idea in hand, we can discuss some of the details
-of how this pattern proceeds.
-
-Implicit Method Access
-----------------------
-
-The job of the :class:`.scoped_session` is simple; hold onto a :class:`.Session`
-for all who ask for it. As a means of producing more transparent access to this
-:class:`.Session`, the :class:`.scoped_session` also includes **proxy behavior**,
-meaning that the registry itself can be treated just like a :class:`.Session`
-directly; when methods are called on this object, they are **proxied** to the
-underlying :class:`.Session` being maintained by the registry::
-
- Session = scoped_session(some_factory)
-
- # equivalent to:
- #
- # session = Session()
- # print(session.scalars(select(MyClass)).all())
- #
- print(Session.scalars(select(MyClass)).all())
-
-The above code accomplishes the same task as that of acquiring the current
-:class:`.Session` by calling upon the registry, then using that :class:`.Session`.
-
-.. _unitofwork_contextual_threadlocal:
-
-Thread-Local Scope
-------------------
-
-Users who are familiar with multithreaded programming will note that representing
-anything as a global variable is usually a bad idea, as it implies that the
-global object will be accessed by many threads concurrently. The :class:`.Session`
-object is entirely designed to be used in a **non-concurrent** fashion, which
-in terms of multithreading means "only in one thread at a time". So our
-above example of :class:`.scoped_session` usage, where the same :class:`.Session`
-object is maintained across multiple calls, suggests that some process needs
-to be in place such that multiple calls across many threads don't actually get
-a handle to the same session. We call this notion **thread local storage**,
-which means, a special object is used that will maintain a distinct object
-per each application thread. Python provides this via the
-`threading.local() <https://docs.python.org/library/threading.html#threading.local>`_
-construct. The :class:`.scoped_session` object by default uses this object
-as storage, so that a single :class:`.Session` is maintained for all who call
-upon the :class:`.scoped_session` registry, but only within the scope of a single
-thread. Callers who call upon the registry in a different thread get a
-:class:`.Session` instance that is local to that other thread.
-
-Using this technique, the :class:`.scoped_session` provides a quick and relatively
-simple (if one is familiar with thread-local storage) way of providing
-a single, global object in an application that is safe to be called upon
-from multiple threads.
-
-The :meth:`.scoped_session.remove` method, as always, removes the current
-:class:`.Session` associated with the thread, if any. However, one advantage of the
-``threading.local()`` object is that if the application thread itself ends, the
-"storage" for that thread is also garbage collected. So it is in fact "safe" to
-use thread local scope with an application that spawns and tears down threads,
-without the need to call :meth:`.scoped_session.remove`. However, the scope
-of transactions themselves, i.e. ending them via :meth:`.Session.commit` or
-:meth:`.Session.rollback`, will usually still be something that must be explicitly
-arranged for at the appropriate time, unless the application actually ties the
-lifespan of a thread to the lifespan of a transaction.
-
-.. _session_lifespan:
-
-Using Thread-Local Scope with Web Applications
-----------------------------------------------
-
-As discussed in the section :ref:`session_faq_whentocreate`, a web application
-is architected around the concept of a **web request**, and integrating
-such an application with the :class:`.Session` usually implies that the :class:`.Session`
-will be associated with that request. As it turns out, most Python web frameworks,
-with notable exceptions such as the asynchronous frameworks Twisted and
-Tornado, use threads in a simple way, such that a particular web request is received,
-processed, and completed within the scope of a single *worker thread*. When
-the request ends, the worker thread is released to a pool of workers where it
-is available to handle another request.
-
-This simple correspondence of web request and thread means that to associate a
-:class:`.Session` with a thread implies it is also associated with the web request
-running within that thread, and vice versa, provided that the :class:`.Session` is
-created only after the web request begins and torn down just before the web request ends.
-So it is a common practice to use :class:`.scoped_session` as a quick way
-to integrate the :class:`.Session` with a web application. The sequence
-diagram below illustrates this flow:
-
-.. sourcecode:: text
-
- Web Server Web Framework SQLAlchemy ORM Code
- -------------- -------------- ------------------------------
- startup -> Web framework # Session registry is established
- initializes Session = scoped_session(sessionmaker())
-
- incoming
- web request -> web request -> # The registry is *optionally*
- starts # called upon explicitly to create
- # a Session local to the thread and/or request
- Session()
-
- # the Session registry can otherwise
- # be used at any time, creating the
- # request-local Session() if not present,
- # or returning the existing one
- Session.execute(select(MyClass)) # ...
-
- Session.add(some_object) # ...
-
- # if data was modified, commit the
- # transaction
- Session.commit()
-
- web request ends -> # the registry is instructed to
- # remove the Session
- Session.remove()
-
- sends output <-
- outgoing web <-
- response
-
-Using the above flow, the process of integrating the :class:`.Session` with the
-web application has exactly two requirements:
-
-1. Create a single :class:`.scoped_session` registry when the web application
- first starts, ensuring that this object is accessible by the rest of the
- application.
-2. Ensure that :meth:`.scoped_session.remove` is called when the web request ends,
- usually by integrating with the web framework's event system to establish
- an "on request end" event.
-
-As noted earlier, the above pattern is **just one potential way** to integrate a :class:`.Session`
-with a web framework, one which in particular makes the significant assumption
-that the **web framework associates web requests with application threads**. It is
-however **strongly recommended that the integration tools provided with the web framework
-itself be used, if available**, instead of :class:`.scoped_session`.
-
-In particular, while using a thread local can be convenient, it is preferable that the :class:`.Session` be
-associated **directly with the request**, rather than with
-the current thread. The next section on custom scopes details a more advanced configuration
-which can combine the usage of :class:`.scoped_session` with direct request based scope, or
-any kind of scope.
-
-Using Custom Created Scopes
----------------------------
-
-The :class:`.scoped_session` object's default behavior of "thread local" scope is only
-one of many options on how to "scope" a :class:`.Session`. A custom scope can be defined
-based on any existing system of getting at "the current thing we are working with".
-
-Suppose a web framework defines a library function ``get_current_request()``. An application
-built using this framework can call this function at any time, and the result will be
-some kind of ``Request`` object that represents the current request being processed.
-If the ``Request`` object is hashable, then this function can be easily integrated with
-:class:`.scoped_session` to associate the :class:`.Session` with the request. Below we illustrate
-this in conjunction with a hypothetical event marker provided by the web framework
-``on_request_end``, which allows code to be invoked whenever a request ends::
-
- from my_web_framework import get_current_request, on_request_end
- from sqlalchemy.orm import scoped_session, sessionmaker
-
- Session = scoped_session(sessionmaker(bind=some_engine), scopefunc=get_current_request)
-
-
- @on_request_end
- def remove_session(req):
- Session.remove()
-
-Above, we instantiate :class:`.scoped_session` in the usual way, except that we pass
-our request-returning function as the "scopefunc". This instructs :class:`.scoped_session`
-to use this function to generate a dictionary key whenever the registry is called upon
-to return the current :class:`.Session`. In this case it is particularly important
-that we ensure a reliable "remove" system is implemented, as this dictionary is not
-otherwise self-managed.
-
-
-Contextual Session API
-----------------------
-
-.. autoclass:: sqlalchemy.orm.scoped_session
- :members:
- :inherited-members:
-
-.. autoclass:: sqlalchemy.util.ScopedRegistry
- :members:
-
-.. autoclass:: sqlalchemy.util.ThreadLocalRegistry
-
-.. autoclass:: sqlalchemy.orm.QueryPropertyDescriptor
+++ /dev/null
-.. _orm_dataclasses_toplevel:
-
-======================================
-Integration with dataclasses and attrs
-======================================
-
-SQLAlchemy as of version 2.0 features "native dataclass" integration where
-an :ref:`Annotated Declarative Table <orm_declarative_mapped_column>`
-mapping may be turned into a Python dataclass_ by adding a single mixin
-or decorator to mapped classes.
-
-.. versionadded:: 2.0 Integrated dataclass creation with ORM Declarative classes
-
-There are also patterns available that allow existing dataclasses to be
-mapped, as well as to map classes instrumented by the
-attrs_ third party integration library.
-
-.. _orm_declarative_native_dataclasses:
-
-Declarative Dataclass Mapping
--------------------------------
-
-SQLAlchemy :ref:`Annotated Declarative Table <orm_declarative_mapped_column>`
-mappings may be augmented with an additional
-mixin class or decorator directive, which will add an additional step to
-the Declarative process after the mapping is complete that will convert
-the mapped class **in-place** into a Python dataclass_, before completing
-the mapping process which applies ORM-specific :term:`instrumentation`
-to the class. The most prominent behavioral addition this provides is
-generation of an ``__init__()`` method with fine-grained control over
-positional and keyword arguments with or without defaults, as well as
-generation of methods like ``__repr__()`` and ``__eq__()``.
-
-From a :pep:`484` typing perspective, the class is recognized
-as having Dataclass-specific behaviors, most notably by taking advantage of :pep:`681`
-"Dataclass Transforms", which allows typing tools to consider the class
-as though it were explicitly decorated using the ``@dataclasses.dataclass``
-decorator.
-
-.. note:: Support for :pep:`681` in typing tools as of **April 4, 2023** is
- limited and is currently known to be supported by Pyright_ as well
- as Mypy_ as of **version 1.2**. Note that Mypy 1.1.1 introduced
- :pep:`681` support but did not correctly accommodate Python descriptors
- which will lead to errors when using SQLAlhcemy's ORM mapping scheme.
-
- .. seealso::
-
- https://peps.python.org/pep-0681/#the-dataclass-transform-decorator - background
- on how libraries like SQLAlchemy enable :pep:`681` support
-
-
-Dataclass conversion may be added to any Declarative class either by adding the
-:class:`_orm.MappedAsDataclass` mixin to a :class:`_orm.DeclarativeBase` class
-hierarchy, or for decorator mapping by using the
-:meth:`_orm.registry.mapped_as_dataclass` class decorator.
-
-The :class:`_orm.MappedAsDataclass` mixin may be applied either
-to the Declarative ``Base`` class or any superclass, as in the example
-below::
-
-
- from sqlalchemy.orm import DeclarativeBase
- from sqlalchemy.orm import Mapped
- from sqlalchemy.orm import mapped_column
- from sqlalchemy.orm import MappedAsDataclass
-
-
- class Base(MappedAsDataclass, DeclarativeBase):
- """subclasses will be converted to dataclasses"""
-
-
- class User(Base):
- __tablename__ = "user_account"
-
- id: Mapped[int] = mapped_column(init=False, primary_key=True)
- name: Mapped[str]
-
-Or may be applied directly to classes that extend from the Declarative base::
-
- from sqlalchemy.orm import DeclarativeBase
- from sqlalchemy.orm import Mapped
- from sqlalchemy.orm import mapped_column
- from sqlalchemy.orm import MappedAsDataclass
-
-
- class Base(DeclarativeBase):
- pass
-
-
- class User(MappedAsDataclass, Base):
- """User class will be converted to a dataclass"""
-
- __tablename__ = "user_account"
-
- id: Mapped[int] = mapped_column(init=False, primary_key=True)
- name: Mapped[str]
-
-When using the decorator form, only the :meth:`_orm.registry.mapped_as_dataclass`
-decorator is supported::
-
- from sqlalchemy.orm import Mapped
- from sqlalchemy.orm import mapped_column
- from sqlalchemy.orm import registry
-
-
- reg = registry()
-
-
- @reg.mapped_as_dataclass
- class User:
- __tablename__ = "user_account"
-
- id: Mapped[int] = mapped_column(init=False, primary_key=True)
- name: Mapped[str]
-
-Class level feature configuration
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Support for dataclasses features is partial. Currently **supported** are
-the ``init``, ``repr``, ``eq``, ``order`` and ``unsafe_hash`` features,
-``match_args`` and ``kw_only`` are supported on Python 3.10+.
-Currently **not supported** are the ``frozen`` and ``slots`` features.
-
-When using the mixin class form with :class:`_orm.MappedAsDataclass`,
-class configuration arguments are passed as class-level parameters::
-
- from sqlalchemy.orm import DeclarativeBase
- from sqlalchemy.orm import Mapped
- from sqlalchemy.orm import mapped_column
- from sqlalchemy.orm import MappedAsDataclass
-
-
- class Base(DeclarativeBase):
- pass
-
-
- class User(MappedAsDataclass, Base, repr=False, unsafe_hash=True):
- """User class will be converted to a dataclass"""
-
- __tablename__ = "user_account"
-
- id: Mapped[int] = mapped_column(init=False, primary_key=True)
- name: Mapped[str]
-
-When using the decorator form with :meth:`_orm.registry.mapped_as_dataclass`,
-class configuration arguments are passed to the decorator directly::
-
- from sqlalchemy.orm import registry
- from sqlalchemy.orm import Mapped
- from sqlalchemy.orm import mapped_column
-
-
- reg = registry()
-
-
- @reg.mapped_as_dataclass(unsafe_hash=True)
- class User:
- """User class will be converted to a dataclass"""
-
- __tablename__ = "user_account"
-
- id: Mapped[int] = mapped_column(init=False, primary_key=True)
- name: Mapped[str]
-
-For background on dataclass class options, see the dataclasses_ documentation
-at `@dataclasses.dataclass <https://docs.python.org/3/library/dataclasses.html#dataclasses.dataclass>`_.
-
-Attribute Configuration
-^^^^^^^^^^^^^^^^^^^^^^^
-
-SQLAlchemy native dataclasses differ from normal dataclasses in that
-attributes to be mapped are described using the :class:`_orm.Mapped`
-generic annotation container in all cases. Mappings follow the same
-forms as those documented at :ref:`orm_declarative_table`, and all
-features of :func:`_orm.mapped_column` and :class:`_orm.Mapped` are supported.
-
-Additionally, ORM attribute configuration constructs including
-:func:`_orm.mapped_column`, :func:`_orm.relationship` and :func:`_orm.composite`
-support **per-attribute field options**, including ``init``, ``default``,
-``default_factory`` and ``repr``. The names of these arguments is fixed
-as specified in :pep:`681`. Functionality is equivalent to dataclasses:
-
-* ``init``, as in :paramref:`_orm.mapped_column.init`,
- :paramref:`_orm.relationship.init`, if False indicates the field should
- not be part of the ``__init__()`` method
-* ``default``, as in :paramref:`_orm.mapped_column.default`,
- :paramref:`_orm.relationship.default`
- indicates a default value for the field as given as a keyword argument
- in the ``__init__()`` method.
-* ``default_factory``, as in :paramref:`_orm.mapped_column.default_factory`,
- :paramref:`_orm.relationship.default_factory`, indicates a callable function
- that will be invoked to generate a new default value for a parameter
- if not passed explicitly to the ``__init__()`` method.
-* ``repr`` True by default, indicates the field should be part of the generated
- ``__repr__()`` method
-
-
-Another key difference from dataclasses is that default values for attributes
-**must** be configured using the ``default`` parameter of the ORM construct,
-such as ``mapped_column(default=None)``. A syntax that resembles dataclass
-syntax which accepts simple Python values as defaults without using
-``@dataclases.field()`` is not supported.
-
-As an example using :func:`_orm.mapped_column`, the mapping below will
-produce an ``__init__()`` method that accepts only the fields ``name`` and
-``fullname``, where ``name`` is required and may be passed positionally,
-and ``fullname`` is optional. The ``id`` field, which we expect to be
-database-generated, is not part of the constructor at all::
-
- from sqlalchemy.orm import Mapped
- from sqlalchemy.orm import mapped_column
- from sqlalchemy.orm import registry
-
- reg = registry()
-
-
- @reg.mapped_as_dataclass
- class User:
- __tablename__ = "user_account"
-
- id: Mapped[int] = mapped_column(init=False, primary_key=True)
- name: Mapped[str]
- fullname: Mapped[str] = mapped_column(default=None)
-
-
- # 'fullname' is optional keyword argument
- u1 = User("name")
-
-Column Defaults
-~~~~~~~~~~~~~~~
-
-In order to accommodate the name overlap of the ``default`` argument with
-the existing :paramref:`_schema.Column.default` parameter of the :class:`_schema.Column`
-construct, the :func:`_orm.mapped_column` construct disambiguates the two
-names by adding a new parameter :paramref:`_orm.mapped_column.insert_default`,
-which will be populated directly into the
-:paramref:`_schema.Column.default` parameter of :class:`_schema.Column`,
-independently of what may be set on
-:paramref:`_orm.mapped_column.default`, which is always used for the
-dataclasses configuration. For example, to configure a datetime column with
-a :paramref:`_schema.Column.default` set to the ``func.utc_timestamp()`` SQL function,
-but where the parameter is optional in the constructor::
-
- from datetime import datetime
-
- from sqlalchemy import func
- from sqlalchemy.orm import Mapped
- from sqlalchemy.orm import mapped_column
- from sqlalchemy.orm import registry
-
- reg = registry()
-
-
- @reg.mapped_as_dataclass
- class User:
- __tablename__ = "user_account"
-
- id: Mapped[int] = mapped_column(init=False, primary_key=True)
- created_at: Mapped[datetime] = mapped_column(
- insert_default=func.utc_timestamp(), default=None
- )
-
-With the above mapping, an ``INSERT`` for a new ``User`` object where no
-parameter for ``created_at`` were passed proceeds as:
-
-.. sourcecode:: pycon+sql
-
- >>> with Session(e) as session:
- ... session.add(User())
- ... session.commit()
- {execsql}BEGIN (implicit)
- INSERT INTO user_account (created_at) VALUES (utc_timestamp())
- [generated in 0.00010s] ()
- COMMIT
-
-
-
-Integration with Annotated
-~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The approach introduced at :ref:`orm_declarative_mapped_column_pep593` illustrates
-how to use :pep:`593` ``Annotated`` objects to package whole
-:func:`_orm.mapped_column` constructs for re-use. This feature is supported
-with the dataclasses feature. One aspect of the feature however requires
-a workaround when working with typing tools, which is that the
-:pep:`681`-specific arguments ``init``, ``default``, ``repr``, and ``default_factory``
-**must** be on the right hand side, packaged into an explicit :func:`_orm.mapped_column`
-construct, in order for the typing tool to interpret the attribute correctly.
-As an example, the approach below will work perfectly fine at runtime,
-however typing tools will consider the ``User()`` construction to be
-invalid, as they do not see the ``init=False`` parameter present::
-
- from typing import Annotated
-
- from sqlalchemy.orm import Mapped
- from sqlalchemy.orm import mapped_column
- from sqlalchemy.orm import registry
-
- # typing tools will ignore init=False here
- intpk = Annotated[int, mapped_column(init=False, primary_key=True)]
-
- reg = registry()
-
-
- @reg.mapped_as_dataclass
- class User:
- __tablename__ = "user_account"
- id: Mapped[intpk]
-
-
- # typing error: Argument missing for parameter "id"
- u1 = User()
-
-Instead, :func:`_orm.mapped_column` must be present on the right side
-as well with an explicit setting for :paramref:`_orm.mapped_column.init`;
-the other arguments can remain within the ``Annotated`` construct::
-
- from typing import Annotated
-
- from sqlalchemy.orm import Mapped
- from sqlalchemy.orm import mapped_column
- from sqlalchemy.orm import registry
-
- intpk = Annotated[int, mapped_column(primary_key=True)]
-
- reg = registry()
-
-
- @reg.mapped_as_dataclass
- class User:
- __tablename__ = "user_account"
-
- # init=False and other pep-681 arguments must be inline
- id: Mapped[intpk] = mapped_column(init=False)
-
-
- u1 = User()
-
-.. _orm_declarative_dc_mixins:
-
-Using mixins and abstract superclasses
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Any mixins or base classes that are used in a :class:`_orm.MappedAsDataclass`
-mapped class which include :class:`_orm.Mapped` attributes must themselves be
-part of a :class:`_orm.MappedAsDataclass`
-hierarchy, such as in the example below using a mixin::
-
-
- class Mixin(MappedAsDataclass):
-
- create_user: Mapped[int] = mapped_column()
- update_user: Mapped[Optional[int]] = mapped_column(default=None, init=False)
-
-
- class Base(DeclarativeBase, MappedAsDataclass):
- pass
-
-
- class User(Base, Mixin):
- __tablename__ = "sys_user"
-
- uid: Mapped[str] = mapped_column(
- String(50), init=False, default_factory=uuid4, primary_key=True
- )
- username: Mapped[str] = mapped_column()
- email: Mapped[str] = mapped_column()
-
-Python type checkers which support :pep:`681` will otherwise not consider
-attributes from non-dataclass mixins to be part of the dataclass.
-
-.. deprecated:: 2.0.8 Using mixins and abstract bases within
- :class:`_orm.MappedAsDataclass` or
- :meth:`_orm.registry.mapped_as_dataclass` hierarchies which are not
- themselves dataclasses is deprecated, as these fields are not supported
- by :pep:`681` as belonging to the dataclass. A warning is emitted for this
- case which will later be an error.
-
- .. seealso::
-
- :ref:`error_dcmx` - background on rationale
-
-
-
-
-Relationship Configuration
-^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The :class:`_orm.Mapped` annotation in combination with
-:func:`_orm.relationship` is used in the same way as described at
-:ref:`relationship_patterns`. When specifying a collection-based
-:func:`_orm.relationship` as an optional keyword argument, the
-:paramref:`_orm.relationship.default_factory` parameter must be passed and it
-must refer to the collection class that's to be used. Many-to-one and
-scalar object references may make use of
-:paramref:`_orm.relationship.default` if the default value is to be ``None``::
-
- from typing import List
-
- from sqlalchemy import ForeignKey
- from sqlalchemy.orm import Mapped
- from sqlalchemy.orm import mapped_column
- from sqlalchemy.orm import registry
- from sqlalchemy.orm import relationship
-
- reg = registry()
-
-
- @reg.mapped_as_dataclass
- class Parent:
- __tablename__ = "parent"
- id: Mapped[int] = mapped_column(primary_key=True)
- children: Mapped[List["Child"]] = relationship(
- default_factory=list, back_populates="parent"
- )
-
-
- @reg.mapped_as_dataclass
- class Child:
- __tablename__ = "child"
- id: Mapped[int] = mapped_column(primary_key=True)
- parent_id: Mapped[int] = mapped_column(ForeignKey("parent.id"))
- parent: Mapped["Parent"] = relationship(default=None)
-
-The above mapping will generate an empty list for ``Parent.children`` when a
-new ``Parent()`` object is constructed without passing ``children``, and
-similarly a ``None`` value for ``Child.parent`` when a new ``Child()`` object
-is constructed without passsing ``parent``.
-
-While the :paramref:`_orm.relationship.default_factory` can be automatically
-derived from the given collection class of the :func:`_orm.relationship`
-itself, this would break compatibility with dataclasses, as the presence
-of :paramref:`_orm.relationship.default_factory` or
-:paramref:`_orm.relationship.default` is what determines if the parameter is
-to be required or optional when rendered into the ``__init__()`` method.
-
-.. _orm_declarative_native_dataclasses_non_mapped_fields:
-
-Using Non-Mapped Dataclass Fields
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-When using Declarative dataclasses, non-mapped fields may be used on the
-class as well, which will be part of the dataclass construction process but
-will not be mapped. Any field that does not use :class:`.Mapped` will
-be ignored by the mapping process. In the example below, the fields
-``ctrl_one`` and ``ctrl_two`` will be part of the instance-level state
-of the object, but will not be persisted by the ORM::
-
-
- from sqlalchemy.orm import Mapped
- from sqlalchemy.orm import mapped_column
- from sqlalchemy.orm import registry
-
- reg = registry()
-
-
- @reg.mapped_as_dataclass
- class Data:
- __tablename__ = "data"
-
- id: Mapped[int] = mapped_column(init=False, primary_key=True)
- status: Mapped[str]
-
- ctrl_one: Optional[str] = None
- ctrl_two: Optional[str] = None
-
-Instance of ``Data`` above can be created as::
-
- d1 = Data(status="s1", ctrl_one="ctrl1", ctrl_two="ctrl2")
-
-A more real world example might be to make use of the Dataclasses
-``InitVar`` feature in conjunction with the ``__post_init__()`` feature to
-receive init-only fields that can be used to compose persisted data.
-In the example below, the ``User``
-class is declared using ``id``, ``name`` and ``password_hash`` as mapped features,
-but makes use of init-only ``password`` and ``repeat_password`` fields to
-represent the user creation process (note: to run this example, replace
-the function ``your_crypt_function_here()`` with a third party crypt
-function, such as `bcrypt <https://pypi.org/project/bcrypt/>`_ or
-`argon2-cffi <https://pypi.org/project/argon2-cffi/>`_)::
-
- from dataclasses import InitVar
- from typing import Optional
-
- from sqlalchemy.orm import Mapped
- from sqlalchemy.orm import mapped_column
- from sqlalchemy.orm import registry
-
- reg = registry()
-
-
- @reg.mapped_as_dataclass
- class User:
- __tablename__ = "user_account"
-
- id: Mapped[int] = mapped_column(init=False, primary_key=True)
- name: Mapped[str]
-
- password: InitVar[str]
- repeat_password: InitVar[str]
-
- password_hash: Mapped[str] = mapped_column(init=False, nullable=False)
-
- def __post_init__(self, password: str, repeat_password: str):
- if password != repeat_password:
- raise ValueError("passwords do not match")
-
- self.password_hash = your_crypt_function_here(password)
-
-The above object is created with parameters ``password`` and
-``repeat_password``, which are consumed up front so that the ``password_hash``
-variable may be generated::
-
- >>> u1 = User(name="some_user", password="xyz", repeat_password="xyz")
- >>> u1.password_hash
- '$6$9ppc... (example crypted string....)'
-
-.. versionchanged:: 2.0.0rc1 When using :meth:`_orm.registry.mapped_as_dataclass`
- or :class:`.MappedAsDataclass`, fields that do not include the
- :class:`.Mapped` annotation may be included, which will be treated as part
- of the resulting dataclass but not be mapped, without the need to
- also indicate the ``__allow_unmapped__`` class attribute. Previous 2.0
- beta releases would require this attribute to be explicitly present,
- even though the purpose of this attribute was only to allow legacy
- ORM typed mappings to continue to function.
-
-.. _dataclasses_pydantic:
-
-Integrating with Alternate Dataclass Providers such as Pydantic
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-SQLAlchemy's :class:`_orm.MappedAsDataclass` class
-and :meth:`_orm.registry.mapped_as_dataclass` method call directly into
-the Python standard library ``dataclasses.dataclass`` class decorator, after
-the declarative mapping process has been applied to the class. This
-function call may be swapped out for alternateive dataclasses providers,
-such as that of Pydantic, using the ``dataclass_callable`` parameter
-accepted by :class:`_orm.MappedAsDataclass` as a class keyword argument
-as well as by :meth:`_orm.registry.mapped_as_dataclass`::
-
- from sqlalchemy.orm import DeclarativeBase
- from sqlalchemy.orm import Mapped
- from sqlalchemy.orm import mapped_column
- from sqlalchemy.orm import MappedAsDataclass
- from sqlalchemy.orm import registry
-
-
- class Base(
- MappedAsDataclass,
- DeclarativeBase,
- dataclass_callable=pydantic.dataclasses.dataclass,
- ):
- pass
-
-
- class User(Base):
- __tablename__ = "user"
-
- id: Mapped[int] = mapped_column(primary_key=True)
- name: Mapped[str]
-
-The above ``User`` class will be applied as a dataclass, using Pydantic's
-``pydantic.dataclasses.dataclasses`` callable. The process is available
-both for mapped classes as well as mixins that extend from
-:class:`_orm.MappedAsDataclass` or which have
-:meth:`_orm.registry.mapped_as_dataclass` applied directly.
-
-.. versionadded:: 2.0.4 Added the ``dataclass_callable`` class and method
- parameters for :class:`_orm.MappedAsDataclass` and
- :meth:`_orm.registry.mapped_as_dataclass`, and adjusted some of the
- dataclass internals to accommodate more strict dataclass functions such as
- that of Pydantic.
-
-
-.. _orm_declarative_dataclasses:
-
-Applying ORM Mappings to an existing dataclass (legacy dataclass use)
----------------------------------------------------------------------
-
-.. legacy::
-
- The approaches described here are superseded by
- the :ref:`orm_declarative_native_dataclasses` feature new in the 2.0
- series of SQLAlchemy. This newer version of the feature builds upon
- the dataclass support first added in version 1.4, which is described
- in this section.
-
-To map an existing dataclass, SQLAlchemy's "inline" declarative directives
-cannot be used directly; ORM directives are assigned using one of three
-techniques:
-
-* Using "Declarative with Imperative Table", the table / column to be mapped
- is defined using a :class:`_schema.Table` object assigned to the
- ``__table__`` attribute of the class; relationships are defined within
- ``__mapper_args__`` dictionary. The class is mapped using the
- :meth:`_orm.registry.mapped` decorator. An example is below at
- :ref:`orm_declarative_dataclasses_imperative_table`.
-
-* Using full "Declarative", the Declarative-interpreted directives such as
- :class:`_schema.Column`, :func:`_orm.relationship` are added to the
- ``.metadata`` dictionary of the ``dataclasses.field()`` construct, where
- they are consumed by the declarative process. The class is again
- mapped using the :meth:`_orm.registry.mapped` decorator. See the example
- below at :ref:`orm_declarative_dataclasses_declarative_table`.
-
-* An "Imperative" mapping can be applied to an existing dataclass using
- the :meth:`_orm.registry.map_imperatively` method to produce the mapping
- in exactly the same way as described at :ref:`orm_imperative_mapping`.
- This is illustrated below at :ref:`orm_imperative_dataclasses`.
-
-The general process by which SQLAlchemy applies mappings to a dataclass
-is the same as that of an ordinary class, but also includes that
-SQLAlchemy will detect class-level attributes that were part of the
-dataclasses declaration process and replace them at runtime with
-the usual SQLAlchemy ORM mapped attributes. The ``__init__`` method that
-would have been generated by dataclasses is left intact, as is the same
-for all the other methods that dataclasses generates such as
-``__eq__()``, ``__repr__()``, etc.
-
-.. _orm_declarative_dataclasses_imperative_table:
-
-Mapping pre-existing dataclasses using Declarative With Imperative Table
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-An example of a mapping using ``@dataclass`` using
-:ref:`orm_imperative_table_configuration` is below. A complete
-:class:`_schema.Table` object is constructed explicitly and assigned to the
-``__table__`` attribute. Instance fields are defined using normal dataclass
-syntaxes. Additional :class:`.MapperProperty`
-definitions such as :func:`.relationship`, are placed in the
-:ref:`__mapper_args__ <orm_declarative_mapper_options>` class-level
-dictionary underneath the ``properties`` key, corresponding to the
-:paramref:`_orm.Mapper.properties` parameter::
-
- from __future__ import annotations
-
- from dataclasses import dataclass, field
- from typing import List, Optional
-
- from sqlalchemy import Column, ForeignKey, Integer, String, Table
- from sqlalchemy.orm import registry, relationship
-
- mapper_registry = registry()
-
-
- @mapper_registry.mapped
- @dataclass
- class User:
- __table__ = Table(
- "user",
- mapper_registry.metadata,
- Column("id", Integer, primary_key=True),
- Column("name", String(50)),
- Column("fullname", String(50)),
- Column("nickname", String(12)),
- )
- id: int = field(init=False)
- name: Optional[str] = None
- fullname: Optional[str] = None
- nickname: Optional[str] = None
- addresses: List[Address] = field(default_factory=list)
-
- __mapper_args__ = { # type: ignore
- "properties": {
- "addresses": relationship("Address"),
- }
- }
-
-
- @mapper_registry.mapped
- @dataclass
- class Address:
- __table__ = Table(
- "address",
- mapper_registry.metadata,
- Column("id", Integer, primary_key=True),
- Column("user_id", Integer, ForeignKey("user.id")),
- Column("email_address", String(50)),
- )
- id: int = field(init=False)
- user_id: int = field(init=False)
- email_address: Optional[str] = None
-
-In the above example, the ``User.id``, ``Address.id``, and ``Address.user_id``
-attributes are defined as ``field(init=False)``. This means that parameters for
-these won't be added to ``__init__()`` methods, but
-:class:`.Session` will still be able to set them after getting their values
-during flush from autoincrement or other default value generator. To
-allow them to be specified in the constructor explicitly, they would instead
-be given a default value of ``None``.
-
-For a :func:`_orm.relationship` to be declared separately, it needs to be
-specified directly within the :paramref:`_orm.Mapper.properties` dictionary
-which itself is specified within the ``__mapper_args__`` dictionary, so that it
-is passed to the constructor for :class:`_orm.Mapper`. An alternative to this
-approach is in the next example.
-
-.. _orm_declarative_dataclasses_declarative_table:
-
-Mapping pre-existing dataclasses using Declarative-style fields
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-.. legacy:: This approach to Declarative mapping with
- dataclasses should be considered as legacy. It will remain supported
- however is unlikely to offer any advantages against the new
- approach detailed at :ref:`orm_declarative_native_dataclasses`.
-
- Note that **mapped_column() is not supported with this use**;
- the :class:`_schema.Column` construct should continue to be used to declare
- table metadata within the ``metadata`` field of ``dataclasses.field()``.
-
-The fully declarative approach requires that :class:`_schema.Column` objects
-are declared as class attributes, which when using dataclasses would conflict
-with the dataclass-level attributes. An approach to combine these together
-is to make use of the ``metadata`` attribute on the ``dataclass.field``
-object, where SQLAlchemy-specific mapping information may be supplied.
-Declarative supports extraction of these parameters when the class
-specifies the attribute ``__sa_dataclass_metadata_key__``. This also
-provides a more succinct method of indicating the :func:`_orm.relationship`
-association::
-
-
- from __future__ import annotations
-
- from dataclasses import dataclass, field
- from typing import List
-
- from sqlalchemy import Column, ForeignKey, Integer, String
- from sqlalchemy.orm import registry, relationship
-
- mapper_registry = registry()
-
-
- @mapper_registry.mapped
- @dataclass
- class User:
- __tablename__ = "user"
-
- __sa_dataclass_metadata_key__ = "sa"
- id: int = field(init=False, metadata={"sa": Column(Integer, primary_key=True)})
- name: str = field(default=None, metadata={"sa": Column(String(50))})
- fullname: str = field(default=None, metadata={"sa": Column(String(50))})
- nickname: str = field(default=None, metadata={"sa": Column(String(12))})
- addresses: List[Address] = field(
- default_factory=list, metadata={"sa": relationship("Address")}
- )
-
-
- @mapper_registry.mapped
- @dataclass
- class Address:
- __tablename__ = "address"
- __sa_dataclass_metadata_key__ = "sa"
- id: int = field(init=False, metadata={"sa": Column(Integer, primary_key=True)})
- user_id: int = field(init=False, metadata={"sa": Column(ForeignKey("user.id"))})
- email_address: str = field(default=None, metadata={"sa": Column(String(50))})
-
-.. _orm_declarative_dataclasses_mixin:
-
-Using Declarative Mixins with pre-existing dataclasses
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-In the section :ref:`orm_mixins_toplevel`, Declarative Mixin classes
-are introduced. One requirement of declarative mixins is that certain
-constructs that can't be easily duplicated must be given as callables,
-using the :class:`_orm.declared_attr` decorator, such as in the
-example at :ref:`orm_declarative_mixins_relationships`::
-
- class RefTargetMixin:
- @declared_attr
- def target_id(cls):
- return Column("target_id", ForeignKey("target.id"))
-
- @declared_attr
- def target(cls):
- return relationship("Target")
-
-This form is supported within the Dataclasses ``field()`` object by using
-a lambda to indicate the SQLAlchemy construct inside the ``field()``.
-Using :func:`_orm.declared_attr` to surround the lambda is optional.
-If we wanted to produce our ``User`` class above where the ORM fields
-came from a mixin that is itself a dataclass, the form would be::
-
- @dataclass
- class UserMixin:
- __tablename__ = "user"
-
- __sa_dataclass_metadata_key__ = "sa"
-
- id: int = field(init=False, metadata={"sa": Column(Integer, primary_key=True)})
-
- addresses: List[Address] = field(
- default_factory=list, metadata={"sa": lambda: relationship("Address")}
- )
-
-
- @dataclass
- class AddressMixin:
- __tablename__ = "address"
- __sa_dataclass_metadata_key__ = "sa"
- id: int = field(init=False, metadata={"sa": Column(Integer, primary_key=True)})
- user_id: int = field(
- init=False, metadata={"sa": lambda: Column(ForeignKey("user.id"))}
- )
- email_address: str = field(default=None, metadata={"sa": Column(String(50))})
-
-
- @mapper_registry.mapped
- class User(UserMixin):
- pass
-
-
- @mapper_registry.mapped
- class Address(AddressMixin):
- pass
-
-.. versionadded:: 1.4.2 Added support for "declared attr" style mixin attributes,
- namely :func:`_orm.relationship` constructs as well as :class:`_schema.Column`
- objects with foreign key declarations, to be used within "Dataclasses
- with Declarative Table" style mappings.
-
-
-
-.. _orm_imperative_dataclasses:
-
-Mapping pre-existing dataclasses using Imperative Mapping
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-As described previously, a class which is set up as a dataclass using the
-``@dataclass`` decorator can then be further decorated using the
-:meth:`_orm.registry.mapped` decorator in order to apply declarative-style
-mapping to the class. As an alternative to using the
-:meth:`_orm.registry.mapped` decorator, we may also pass the class through the
-:meth:`_orm.registry.map_imperatively` method instead, so that we may pass all
-:class:`_schema.Table` and :class:`_orm.Mapper` configuration imperatively to
-the function rather than having them defined on the class itself as class
-variables::
-
- from __future__ import annotations
-
- from dataclasses import dataclass
- from dataclasses import field
- from typing import List
-
- from sqlalchemy import Column
- from sqlalchemy import ForeignKey
- from sqlalchemy import Integer
- from sqlalchemy import MetaData
- from sqlalchemy import String
- from sqlalchemy import Table
- from sqlalchemy.orm import registry
- from sqlalchemy.orm import relationship
-
- mapper_registry = registry()
-
-
- @dataclass
- class User:
- id: int = field(init=False)
- name: str = None
- fullname: str = None
- nickname: str = None
- addresses: List[Address] = field(default_factory=list)
-
-
- @dataclass
- class Address:
- id: int = field(init=False)
- user_id: int = field(init=False)
- email_address: str = None
-
-
- metadata_obj = MetaData()
-
- user = Table(
- "user",
- metadata_obj,
- Column("id", Integer, primary_key=True),
- Column("name", String(50)),
- Column("fullname", String(50)),
- Column("nickname", String(12)),
- )
-
- address = Table(
- "address",
- metadata_obj,
- Column("id", Integer, primary_key=True),
- Column("user_id", Integer, ForeignKey("user.id")),
- Column("email_address", String(50)),
- )
-
- mapper_registry.map_imperatively(
- User,
- user,
- properties={
- "addresses": relationship(Address, backref="user", order_by=address.c.id),
- },
- )
-
- mapper_registry.map_imperatively(Address, address)
-
-.. _orm_declarative_attrs_imperative_table:
-
-Applying ORM mappings to an existing attrs class
--------------------------------------------------
-
-The attrs_ library is a popular third party library that provides similar
-features as dataclasses, with many additional features provided not
-found in ordinary dataclasses.
-
-A class augmented with attrs_ uses the ``@define`` decorator. This decorator
-initiates a process to scan the class for attributes that define the class'
-behavior, which are then used to generate methods, documentation, and
-annotations.
-
-The SQLAlchemy ORM supports mapping an attrs_ class using **Declarative with
-Imperative Table** or **Imperative** mapping. The general form of these two
-styles is fully equivalent to the
-:ref:`orm_declarative_dataclasses_declarative_table` and
-:ref:`orm_declarative_dataclasses_imperative_table` mapping forms used with
-dataclasses, where the inline attribute directives used by dataclasses or attrs
-are unchanged, and SQLAlchemy's table-oriented instrumentation is applied at
-runtime.
-
-The ``@define`` decorator of attrs_ by default replaces the annotated class
-with a new __slots__ based class, which is not supported. When using the old
-style annotation ``@attr.s`` or using ``define(slots=False)``, the class
-does not get replaced. Furthermore attrs removes its own class-bound attributes
-after the decorator runs, so that SQLAlchemy's mapping process takes over these
-attributes without any issue. Both decorators, ``@attr.s`` and ``@define(slots=False)``
-work with SQLAlchemy.
-
-Mapping attrs with Declarative "Imperative Table"
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-In the "Declarative with Imperative Table" style, a :class:`_schema.Table`
-object is declared inline with the declarative class. The
-``@define`` decorator is applied to the class first, then the
-:meth:`_orm.registry.mapped` decorator second::
-
- from __future__ import annotations
-
- from typing import List
- from typing import Optional
-
- from attrs import define
- from sqlalchemy import Column
- from sqlalchemy import ForeignKey
- from sqlalchemy import Integer
- from sqlalchemy import MetaData
- from sqlalchemy import String
- from sqlalchemy import Table
- from sqlalchemy.orm import Mapped
- from sqlalchemy.orm import registry
- from sqlalchemy.orm import relationship
-
- mapper_registry = registry()
-
-
- @mapper_registry.mapped
- @define(slots=False)
- class User:
- __table__ = Table(
- "user",
- mapper_registry.metadata,
- Column("id", Integer, primary_key=True),
- Column("name", String(50)),
- Column("FullName", String(50), key="fullname"),
- Column("nickname", String(12)),
- )
- id: Mapped[int]
- name: Mapped[str]
- fullname: Mapped[str]
- nickname: Mapped[str]
- addresses: Mapped[List[Address]]
-
- __mapper_args__ = { # type: ignore
- "properties": {
- "addresses": relationship("Address"),
- }
- }
-
-
- @mapper_registry.mapped
- @define(slots=False)
- class Address:
- __table__ = Table(
- "address",
- mapper_registry.metadata,
- Column("id", Integer, primary_key=True),
- Column("user_id", Integer, ForeignKey("user.id")),
- Column("email_address", String(50)),
- )
- id: Mapped[int]
- user_id: Mapped[int]
- email_address: Mapped[Optional[str]]
-
-.. note:: The ``attrs`` ``slots=True`` option, which enables ``__slots__`` on
- a mapped class, cannot be used with SQLAlchemy mappings without fully
- implementing alternative
- :ref:`attribute instrumentation <examples_instrumentation>`, as mapped
- classes normally rely upon direct access to ``__dict__`` for state storage.
- Behavior is undefined when this option is present.
-
-
-
-Mapping attrs with Imperative Mapping
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Just as is the case with dataclasses, we can make use of
-:meth:`_orm.registry.map_imperatively` to map an existing ``attrs`` class
-as well::
-
- from __future__ import annotations
-
- from typing import List
-
- from attrs import define
- from sqlalchemy import Column
- from sqlalchemy import ForeignKey
- from sqlalchemy import Integer
- from sqlalchemy import MetaData
- from sqlalchemy import String
- from sqlalchemy import Table
- from sqlalchemy.orm import registry
- from sqlalchemy.orm import relationship
-
- mapper_registry = registry()
-
-
- @define(slots=False)
- class User:
- id: int
- name: str
- fullname: str
- nickname: str
- addresses: List[Address]
-
-
- @define(slots=False)
- class Address:
- id: int
- user_id: int
- email_address: Optional[str]
-
-
- metadata_obj = MetaData()
-
- user = Table(
- "user",
- metadata_obj,
- Column("id", Integer, primary_key=True),
- Column("name", String(50)),
- Column("fullname", String(50)),
- Column("nickname", String(12)),
- )
-
- address = Table(
- "address",
- metadata_obj,
- Column("id", Integer, primary_key=True),
- Column("user_id", Integer, ForeignKey("user.id")),
- Column("email_address", String(50)),
- )
-
- mapper_registry.map_imperatively(
- User,
- user,
- properties={
- "addresses": relationship(Address, backref="user", order_by=address.c.id),
- },
- )
-
- mapper_registry.map_imperatively(Address, address)
-
-The above form is equivalent to the previous example using
-Declarative with Imperative Table.
-
-
-
-.. _dataclass: https://docs.python.org/3/library/dataclasses.html
-.. _dataclasses: https://docs.python.org/3/library/dataclasses.html
-.. _attrs: https://pypi.org/project/attrs/
-.. _mypy: https://mypy.readthedocs.io/en/stable/
-.. _pyright: https://github.com/microsoft/pyright
+++ /dev/null
-.. _examples_toplevel:
-
-============
-ORM Examples
-============
-
-The SQLAlchemy distribution includes a variety of code examples illustrating
-a select set of patterns, some typical and some not so typical. All are
-runnable and can be found in the ``/examples`` directory of the
-distribution. Descriptions and source code for all can be found here.
-
-Additional SQLAlchemy examples, some user contributed, are available on the
-wiki at `<https://www.sqlalchemy.org/trac/wiki/UsageRecipes>`_.
-
-
-Mapping Recipes
-===============
-
-.. _examples_adjacencylist:
-
-Adjacency List
---------------
-
-.. automodule:: examples.adjacency_list
-
-.. _examples_associations:
-
-Associations
-------------
-
-.. automodule:: examples.association
-
-.. _examples_asyncio:
-
-Asyncio Integration
--------------------
-
-.. automodule:: examples.asyncio
-
-Directed Graphs
----------------
-
-.. automodule:: examples.graphs
-
-Dynamic Relations as Dictionaries
----------------------------------
-
-.. automodule:: examples.dynamic_dict
-
-.. _examples_generic_associations:
-
-Generic Associations
---------------------
-
-.. automodule:: examples.generic_associations
-
-
-Materialized Paths
-------------------
-
-.. automodule:: examples.materialized_paths
-
-Nested Sets
------------
-
-.. automodule:: examples.nested_sets
-
-.. _examples_performance:
-
-Performance
------------
-
-.. automodule:: examples.performance
-
-
-.. _examples_spaceinvaders:
-
-Space Invaders
---------------
-
-.. automodule:: examples.space_invaders
-
-
-.. _examples_versioning:
-
-Versioning Objects
-------------------
-
-.. _examples_versioned_history:
-
-Versioning with a History Table
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-.. automodule:: examples.versioned_history
-
-.. _examples_versioned_rows:
-
-Versioning using Temporal Rows
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-.. automodule:: examples.versioned_rows
-
-.. _examples_vertical_tables:
-
-Vertical Attribute Mapping
---------------------------
-
-.. automodule:: examples.vertical
-
-
-.. _examples_inheritance:
-
-Inheritance Mapping Recipes
-===========================
-
-Basic Inheritance Mappings
---------------------------
-
-.. automodule:: examples.inheritance
-
-Special APIs
-============
-
-.. _examples_instrumentation:
-
-Attribute Instrumentation
--------------------------
-
-.. automodule:: examples.custom_attributes
-
-.. _examples_sharding:
-
-Horizontal Sharding
--------------------
-
-.. automodule:: examples.sharding
-
-Extending the ORM
-=================
-
-.. _examples_session_orm_events:
-
-ORM Query Events
------------------
-
-.. automodule:: examples.extending_query
-
-.. _examples_caching:
-
-Dogpile Caching
----------------
-
-.. automodule:: examples.dogpile_caching
-
+++ /dev/null
-.. _associationproxy_toplevel:
-
-Association Proxy
-=================
-
-.. module:: sqlalchemy.ext.associationproxy
-
-``associationproxy`` is used to create a read/write view of a
-target attribute across a relationship. It essentially conceals
-the usage of a "middle" attribute between two endpoints, and
-can be used to cherry-pick fields from a collection of
-related objects or to reduce the verbosity of using the association
-object pattern. Applied creatively, the association proxy allows
-the construction of sophisticated collections and dictionary
-views of virtually any geometry, persisted to the database using
-standard, transparently configured relational patterns.
-
-.. _associationproxy_scalar_collections:
-
-Simplifying Scalar Collections
-------------------------------
-
-Consider a many-to-many mapping between two classes, ``User`` and ``Keyword``.
-Each ``User`` can have any number of ``Keyword`` objects, and vice-versa
-(the many-to-many pattern is described at :ref:`relationships_many_to_many`).
-The example below illustrates this pattern in the same way, with the
-exception of an extra attribute added to the ``User`` class called
-``User.keywords``::
-
- from __future__ import annotations
-
- from typing import Final
-
- from sqlalchemy import Column
- from sqlalchemy import ForeignKey
- from sqlalchemy import Integer
- from sqlalchemy import String
- from sqlalchemy import Table
- from sqlalchemy.orm import DeclarativeBase
- from sqlalchemy.orm import Mapped
- from sqlalchemy.orm import mapped_column
- from sqlalchemy.orm import relationship
- from sqlalchemy.ext.associationproxy import association_proxy
- from sqlalchemy.ext.associationproxy import AssociationProxy
-
-
- class Base(DeclarativeBase):
- pass
-
-
- class User(Base):
- __tablename__ = "user"
- id: Mapped[int] = mapped_column(primary_key=True)
- name: Mapped[str] = mapped_column(String(64))
- kw: Mapped[List[Keyword]] = relationship(secondary=lambda: user_keyword_table)
-
- def __init__(self, name: str):
- self.name = name
-
- # proxy the 'keyword' attribute from the 'kw' relationship
- keywords: AssociationProxy[List[str]] = association_proxy("kw", "keyword")
-
-
- class Keyword(Base):
- __tablename__ = "keyword"
- id: Mapped[int] = mapped_column(primary_key=True)
- keyword: Mapped[str] = mapped_column(String(64))
-
- def __init__(self, keyword: str):
- self.keyword = keyword
-
-
- user_keyword_table: Final[Table] = Table(
- "user_keyword",
- Base.metadata,
- Column("user_id", Integer, ForeignKey("user.id"), primary_key=True),
- Column("keyword_id", Integer, ForeignKey("keyword.id"), primary_key=True),
- )
-
-In the above example, :func:`.association_proxy` is applied to the ``User``
-class to produce a "view" of the ``kw`` relationship, which exposes the string
-value of ``.keyword`` associated with each ``Keyword`` object. It also
-creates new ``Keyword`` objects transparently when strings are added to the
-collection::
-
- >>> user = User("jek")
- >>> user.keywords.append("cheese-inspector")
- >>> user.keywords.append("snack-ninja")
- >>> print(user.keywords)
- ['cheese-inspector', 'snack-ninja']
-
-To understand the mechanics of this, first review the behavior of
-``User`` and ``Keyword`` without using the ``.keywords`` association proxy.
-Normally, reading and manipulating the collection of "keyword" strings associated
-with ``User`` requires traversal from each collection element to the ``.keyword``
-attribute, which can be awkward. The example below illustrates the identical
-series of operations applied without using the association proxy::
-
- >>> # identical operations without using the association proxy
- >>> user = User("jek")
- >>> user.kw.append(Keyword("cheese-inspector"))
- >>> user.kw.append(Keyword("snack-ninja"))
- >>> print([keyword.keyword for keyword in user.kw])
- ['cheese-inspector', 'snack-ninja']
-
-The :class:`.AssociationProxy` object produced by the :func:`.association_proxy` function
-is an instance of a `Python descriptor <https://docs.python.org/howto/descriptor.html>`_,
-and is not considered to be "mapped" by the :class:`.Mapper` in any way. Therefore,
-it's always indicated inline within the class definition of the mapped class,
-regardless of whether Declarative or Imperative mappings are used.
-
-The proxy functions by operating upon the underlying mapped attribute
-or collection in response to operations, and changes made via the proxy are immediately
-apparent in the mapped attribute, as well as vice versa. The underlying
-attribute remains fully accessible.
-
-When first accessed, the association proxy performs introspection
-operations on the target collection so that its behavior corresponds correctly.
-Details such as if the locally proxied attribute is a collection (as is typical)
-or a scalar reference, as well as if the collection acts like a set, list,
-or dictionary is taken into account, so that the proxy should act just like
-the underlying collection or attribute does.
-
-.. _associationproxy_creator:
-
-Creation of New Values
-----------------------
-
-When a list ``append()`` event (or set ``add()``, dictionary ``__setitem__()``,
-or scalar assignment event) is intercepted by the association proxy, it
-instantiates a new instance of the "intermediary" object using its constructor,
-passing as a single argument the given value. In our example above, an
-operation like::
-
- user.keywords.append("cheese-inspector")
-
-Is translated by the association proxy into the operation::
-
- user.kw.append(Keyword("cheese-inspector"))
-
-The example works here because we have designed the constructor for ``Keyword``
-to accept a single positional argument, ``keyword``. For those cases where a
-single-argument constructor isn't feasible, the association proxy's creational
-behavior can be customized using the :paramref:`.association_proxy.creator`
-argument, which references a callable (i.e. Python function) that will produce
-a new object instance given the singular argument. Below we illustrate this
-using a lambda as is typical::
-
- class User(Base):
- ...
-
- # use Keyword(keyword=kw) on append() events
- keywords: AssociationProxy[List[str]] = association_proxy(
- "kw", "keyword", creator=lambda kw: Keyword(keyword=kw)
- )
-
-The ``creator`` function accepts a single argument in the case of a list-
-or set- based collection, or a scalar attribute. In the case of a dictionary-based
-collection, it accepts two arguments, "key" and "value". An example
-of this is below in :ref:`proxying_dictionaries`.
-
-Simplifying Association Objects
--------------------------------
-
-The "association object" pattern is an extended form of a many-to-many
-relationship, and is described at :ref:`association_pattern`. Association
-proxies are useful for keeping "association objects" out of the way during
-regular use.
-
-Suppose our ``user_keyword`` table above had additional columns
-which we'd like to map explicitly, but in most cases we don't
-require direct access to these attributes. Below, we illustrate
-a new mapping which introduces the ``UserKeywordAssociation`` class, which
-is mapped to the ``user_keyword`` table illustrated earlier.
-This class adds an additional column ``special_key``, a value which
-we occasionally want to access, but not in the usual case. We
-create an association proxy on the ``User`` class called
-``keywords``, which will bridge the gap from the ``user_keyword_associations``
-collection of ``User`` to the ``.keyword`` attribute present on each
-``UserKeywordAssociation``::
-
- from __future__ import annotations
-
- from typing import List
- from typing import Optional
-
- from sqlalchemy import ForeignKey
- from sqlalchemy import String
- from sqlalchemy.ext.associationproxy import association_proxy
- from sqlalchemy.ext.associationproxy import AssociationProxy
- from sqlalchemy.orm import DeclarativeBase
- from sqlalchemy.orm import Mapped
- from sqlalchemy.orm import mapped_column
- from sqlalchemy.orm import relationship
-
-
- class Base(DeclarativeBase):
- pass
-
-
- class User(Base):
- __tablename__ = "user"
-
- id: Mapped[int] = mapped_column(primary_key=True)
- name: Mapped[str] = mapped_column(String(64))
-
- user_keyword_associations: Mapped[List[UserKeywordAssociation]] = relationship(
- back_populates="user",
- cascade="all, delete-orphan",
- )
-
- # association proxy of "user_keyword_associations" collection
- # to "keyword" attribute
- keywords: AssociationProxy[List[Keyword]] = association_proxy(
- "user_keyword_associations",
- "keyword",
- creator=lambda keyword_obj: UserKeywordAssociation(keyword=keyword_obj),
- )
-
- def __init__(self, name: str):
- self.name = name
-
-
- class UserKeywordAssociation(Base):
- __tablename__ = "user_keyword"
- user_id: Mapped[int] = mapped_column(ForeignKey("user.id"), primary_key=True)
- keyword_id: Mapped[int] = mapped_column(ForeignKey("keyword.id"), primary_key=True)
- special_key: Mapped[Optional[str]] = mapped_column(String(50))
-
- user: Mapped[User] = relationship(back_populates="user_keyword_associations")
-
- keyword: Mapped[Keyword] = relationship()
-
-
- class Keyword(Base):
- __tablename__ = "keyword"
- id: Mapped[int] = mapped_column(primary_key=True)
- keyword: Mapped[str] = mapped_column("keyword", String(64))
-
- def __init__(self, keyword: str):
- self.keyword = keyword
-
- def __repr__(self) -> str:
- return f"Keyword({self.keyword!r})"
-
-With the above configuration, we can operate upon the ``.keywords`` collection
-of each ``User`` object, each of which exposes a collection of ``Keyword``
-objects that are obtained from the underlying ``UserKeywordAssociation`` elements::
-
- >>> user = User("log")
- >>> for kw in (Keyword("new_from_blammo"), Keyword("its_big")):
- ... user.keywords.append(kw)
- >>> print(user.keywords)
- [Keyword('new_from_blammo'), Keyword('its_big')]
-
-This example is in contrast to the example illustrated previously at
-:ref:`associationproxy_scalar_collections`, where the association proxy exposed
-a collection of strings, rather than a collection of composed objects.
-In this case, each ``.keywords.append()`` operation is equivalent to::
-
- >>> user.user_keyword_associations.append(
- ... UserKeywordAssociation(keyword=Keyword("its_heavy"))
- ... )
-
-The ``UserKeywordAssociation`` object has two attributes that are both
-populated within the scope of the ``append()`` operation of the association
-proxy; ``.keyword``, which refers to the
-``Keyword`` object, and ``.user``, which refers to the ``User`` object.
-The ``.keyword`` attribute is populated first, as the association proxy
-generates a new ``UserKeywordAssociation`` object in response to the ``.append()``
-operation, assigning the given ``Keyword`` instance to the ``.keyword``
-attribute. Then, as the ``UserKeywordAssociation`` object is appended to the
-``User.user_keyword_associations`` collection, the ``UserKeywordAssociation.user`` attribute,
-configured as ``back_populates`` for ``User.user_keyword_associations``, is initialized
-upon the given ``UserKeywordAssociation`` instance to refer to the parent ``User``
-receiving the append operation. The ``special_key``
-argument above is left at its default value of ``None``.
-
-For those cases where we do want ``special_key`` to have a value, we
-create the ``UserKeywordAssociation`` object explicitly. Below we assign all
-three attributes, wherein the assignment of ``.user`` during
-construction, has the effect of appending the new ``UserKeywordAssociation`` to
-the ``User.user_keyword_associations`` collection (via the relationship)::
-
- >>> UserKeywordAssociation(
- ... keyword=Keyword("its_wood"), user=user, special_key="my special key"
- ... )
-
-The association proxy returns to us a collection of ``Keyword`` objects represented
-by all these operations::
-
- >>> print(user.keywords)
- [Keyword('new_from_blammo'), Keyword('its_big'), Keyword('its_heavy'), Keyword('its_wood')]
-
-.. _proxying_dictionaries:
-
-Proxying to Dictionary Based Collections
-----------------------------------------
-
-The association proxy can proxy to dictionary based collections as well. SQLAlchemy
-mappings usually use the :func:`.attribute_keyed_dict` collection type to
-create dictionary collections, as well as the extended techniques described in
-:ref:`dictionary_collections`.
-
-The association proxy adjusts its behavior when it detects the usage of a
-dictionary-based collection. When new values are added to the dictionary, the
-association proxy instantiates the intermediary object by passing two
-arguments to the creation function instead of one, the key and the value. As
-always, this creation function defaults to the constructor of the intermediary
-class, and can be customized using the ``creator`` argument.
-
-Below, we modify our ``UserKeywordAssociation`` example such that the ``User.user_keyword_associations``
-collection will now be mapped using a dictionary, where the ``UserKeywordAssociation.special_key``
-argument will be used as the key for the dictionary. We also apply a ``creator``
-argument to the ``User.keywords`` proxy so that these values are assigned appropriately
-when new elements are added to the dictionary::
-
- from __future__ import annotations
- from typing import Dict
-
- from sqlalchemy import ForeignKey
- from sqlalchemy import String
- from sqlalchemy.ext.associationproxy import association_proxy
- from sqlalchemy.ext.associationproxy import AssociationProxy
- from sqlalchemy.orm import DeclarativeBase
- from sqlalchemy.orm import Mapped
- from sqlalchemy.orm import mapped_column
- from sqlalchemy.orm import relationship
- from sqlalchemy.orm.collections import attribute_keyed_dict
-
-
- class Base(DeclarativeBase):
- pass
-
-
- class User(Base):
- __tablename__ = "user"
- id: Mapped[int] = mapped_column(primary_key=True)
- name: Mapped[str] = mapped_column(String(64))
-
- # user/user_keyword_associations relationship, mapping
- # user_keyword_associations with a dictionary against "special_key" as key.
- user_keyword_associations: Mapped[Dict[str, UserKeywordAssociation]] = relationship(
- back_populates="user",
- collection_class=attribute_keyed_dict("special_key"),
- cascade="all, delete-orphan",
- )
- # proxy to 'user_keyword_associations', instantiating
- # UserKeywordAssociation assigning the new key to 'special_key',
- # values to 'keyword'.
- keywords: AssociationProxy[Dict[str, Keyword]] = association_proxy(
- "user_keyword_associations",
- "keyword",
- creator=lambda k, v: UserKeywordAssociation(special_key=k, keyword=v),
- )
-
- def __init__(self, name: str):
- self.name = name
-
-
- class UserKeywordAssociation(Base):
- __tablename__ = "user_keyword"
- user_id: Mapped[int] = mapped_column(ForeignKey("user.id"), primary_key=True)
- keyword_id: Mapped[int] = mapped_column(ForeignKey("keyword.id"), primary_key=True)
- special_key: Mapped[str]
-
- user: Mapped[User] = relationship(
- back_populates="user_keyword_associations",
- )
- keyword: Mapped[Keyword] = relationship()
-
-
- class Keyword(Base):
- __tablename__ = "keyword"
- id: Mapped[int] = mapped_column(primary_key=True)
- keyword: Mapped[str] = mapped_column(String(64))
-
- def __init__(self, keyword: str):
- self.keyword = keyword
-
- def __repr__(self) -> str:
- return f"Keyword({self.keyword!r})"
-
-We illustrate the ``.keywords`` collection as a dictionary, mapping the
-``UserKeywordAssociation.special_key`` value to ``Keyword`` objects::
-
- >>> user = User("log")
-
- >>> user.keywords["sk1"] = Keyword("kw1")
- >>> user.keywords["sk2"] = Keyword("kw2")
-
- >>> print(user.keywords)
- {'sk1': Keyword('kw1'), 'sk2': Keyword('kw2')}
-
-.. _composite_association_proxy:
-
-Composite Association Proxies
------------------------------
-
-Given our previous examples of proxying from relationship to scalar
-attribute, proxying across an association object, and proxying dictionaries,
-we can combine all three techniques together to give ``User``
-a ``keywords`` dictionary that deals strictly with the string value
-of ``special_key`` mapped to the string ``keyword``. Both the ``UserKeywordAssociation``
-and ``Keyword`` classes are entirely concealed. This is achieved by building
-an association proxy on ``User`` that refers to an association proxy
-present on ``UserKeywordAssociation``::
-
- from __future__ import annotations
-
- from sqlalchemy import ForeignKey
- from sqlalchemy import String
- from sqlalchemy.ext.associationproxy import association_proxy
- from sqlalchemy.ext.associationproxy import AssociationProxy
- from sqlalchemy.orm import DeclarativeBase
- from sqlalchemy.orm import Mapped
- from sqlalchemy.orm import mapped_column
- from sqlalchemy.orm import relationship
- from sqlalchemy.orm.collections import attribute_keyed_dict
-
-
- class Base(DeclarativeBase):
- pass
-
-
- class User(Base):
- __tablename__ = "user"
- id: Mapped[int] = mapped_column(primary_key=True)
- name: Mapped[str] = mapped_column(String(64))
-
- user_keyword_associations: Mapped[Dict[str, UserKeywordAssociation]] = relationship(
- back_populates="user",
- collection_class=attribute_keyed_dict("special_key"),
- cascade="all, delete-orphan",
- )
- # the same 'user_keyword_associations'->'keyword' proxy as in
- # the basic dictionary example.
- keywords: AssociationProxy[Dict[str, str]] = association_proxy(
- "user_keyword_associations",
- "keyword",
- creator=lambda k, v: UserKeywordAssociation(special_key=k, keyword=v),
- )
-
- def __init__(self, name: str):
- self.name = name
-
-
- class UserKeywordAssociation(Base):
- __tablename__ = "user_keyword"
- user_id: Mapped[int] = mapped_column(ForeignKey("user.id"), primary_key=True)
- keyword_id: Mapped[int] = mapped_column(ForeignKey("keyword.id"), primary_key=True)
- special_key: Mapped[str] = mapped_column(String(64))
- user: Mapped[User] = relationship(
- back_populates="user_keyword_associations",
- )
-
- # the relationship to Keyword is now called
- # 'kw'
- kw: Mapped[Keyword] = relationship()
-
- # 'keyword' is changed to be a proxy to the
- # 'keyword' attribute of 'Keyword'
- keyword: AssociationProxy[Dict[str, str]] = association_proxy("kw", "keyword")
-
-
- class Keyword(Base):
- __tablename__ = "keyword"
- id: Mapped[int] = mapped_column(primary_key=True)
- keyword: Mapped[str] = mapped_column(String(64))
-
- def __init__(self, keyword: str):
- self.keyword = keyword
-
-``User.keywords`` is now a dictionary of string to string, where
-``UserKeywordAssociation`` and ``Keyword`` objects are created and removed for us
-transparently using the association proxy. In the example below, we illustrate
-usage of the assignment operator, also appropriately handled by the
-association proxy, to apply a dictionary value to the collection at once::
-
- >>> user = User("log")
- >>> user.keywords = {"sk1": "kw1", "sk2": "kw2"}
- >>> print(user.keywords)
- {'sk1': 'kw1', 'sk2': 'kw2'}
-
- >>> user.keywords["sk3"] = "kw3"
- >>> del user.keywords["sk2"]
- >>> print(user.keywords)
- {'sk1': 'kw1', 'sk3': 'kw3'}
-
- >>> # illustrate un-proxied usage
- ... print(user.user_keyword_associations["sk3"].kw)
- <__main__.Keyword object at 0x12ceb90>
-
-One caveat with our example above is that because ``Keyword`` objects are created
-for each dictionary set operation, the example fails to maintain uniqueness for
-the ``Keyword`` objects on their string name, which is a typical requirement for
-a tagging scenario such as this one. For this use case the recipe
-`UniqueObject <https://www.sqlalchemy.org/trac/wiki/UsageRecipes/UniqueObject>`_, or
-a comparable creational strategy, is
-recommended, which will apply a "lookup first, then create" strategy to the constructor
-of the ``Keyword`` class, so that an already existing ``Keyword`` is returned if the
-given name is already present.
-
-Querying with Association Proxies
----------------------------------
-
-The :class:`.AssociationProxy` features simple SQL construction capabilities
-which work at the class level in a similar way as other ORM-mapped attributes,
-and provide rudimentary filtering support primarily based on the
-SQL ``EXISTS`` keyword.
-
-
-.. note:: The primary purpose of the association proxy extension is to allow
- for improved persistence and object-access patterns with mapped object
- instances that are already loaded. The class-bound querying feature
- is of limited use and will not replace the need to refer to the underlying
- attributes when constructing SQL queries with JOINs, eager loading
- options, etc.
-
-For this section, assume a class with both an association proxy
-that refers to a column, as well as an association proxy that refers
-to a related object, as in the example mapping below::
-
- from __future__ import annotations
- from sqlalchemy import Column, ForeignKey, Integer, String
- from sqlalchemy.ext.associationproxy import association_proxy, AssociationProxy
- from sqlalchemy.orm import DeclarativeBase, relationship
- from sqlalchemy.orm.collections import attribute_keyed_dict
- from sqlalchemy.orm.collections import Mapped
-
-
- class Base(DeclarativeBase):
- pass
-
-
- class User(Base):
- __tablename__ = "user"
- id: Mapped[int] = mapped_column(primary_key=True)
- name: Mapped[str] = mapped_column(String(64))
-
- user_keyword_associations: Mapped[UserKeywordAssociation] = relationship(
- cascade="all, delete-orphan",
- )
-
- # object-targeted association proxy
- keywords: AssociationProxy[List[Keyword]] = association_proxy(
- "user_keyword_associations",
- "keyword",
- )
-
- # column-targeted association proxy
- special_keys: AssociationProxy[List[str]] = association_proxy(
- "user_keyword_associations", "special_key"
- )
-
-
- class UserKeywordAssociation(Base):
- __tablename__ = "user_keyword"
- user_id: Mapped[int] = mapped_column(ForeignKey("user.id"), primary_key=True)
- keyword_id: Mapped[int] = mapped_column(ForeignKey("keyword.id"), primary_key=True)
- special_key: Mapped[str] = mapped_column(String(64))
- keyword: Mapped[Keyword] = relationship()
-
-
- class Keyword(Base):
- __tablename__ = "keyword"
- id: Mapped[int] = mapped_column(primary_key=True)
- keyword: Mapped[str] = mapped_column(String(64))
-
-The SQL generated takes the form of a correlated subquery against
-the EXISTS SQL operator so that it can be used in a WHERE clause without
-the need for additional modifications to the enclosing query. If the
-immediate target of an association proxy is a **mapped column expression**,
-standard column operators can be used which will be embedded in the subquery.
-For example a straight equality operator:
-
-.. sourcecode:: pycon+sql
-
- >>> print(session.scalars(select(User).where(User.special_keys == "jek")))
- {printsql}SELECT "user".id AS user_id, "user".name AS user_name
- FROM "user"
- WHERE EXISTS (SELECT 1
- FROM user_keyword
- WHERE "user".id = user_keyword.user_id AND user_keyword.special_key = :special_key_1)
-
-a LIKE operator:
-
-.. sourcecode:: pycon+sql
-
- >>> print(session.scalars(select(User).where(User.special_keys.like("%jek"))))
- {printsql}SELECT "user".id AS user_id, "user".name AS user_name
- FROM "user"
- WHERE EXISTS (SELECT 1
- FROM user_keyword
- WHERE "user".id = user_keyword.user_id AND user_keyword.special_key LIKE :special_key_1)
-
-For association proxies where the immediate target is a **related object or collection,
-or another association proxy or attribute on the related object**, relationship-oriented
-operators can be used instead, such as :meth:`_orm.PropComparator.has` and
-:meth:`_orm.PropComparator.any`. The ``User.keywords`` attribute is in fact
-two association proxies linked together, so when using this proxy for generating
-SQL phrases, we get two levels of EXISTS subqueries:
-
-.. sourcecode:: pycon+sql
-
- >>> print(session.scalars(select(User).where(User.keywords.any(Keyword.keyword == "jek"))))
- {printsql}SELECT "user".id AS user_id, "user".name AS user_name
- FROM "user"
- WHERE EXISTS (SELECT 1
- FROM user_keyword
- WHERE "user".id = user_keyword.user_id AND (EXISTS (SELECT 1
- FROM keyword
- WHERE keyword.id = user_keyword.keyword_id AND keyword.keyword = :keyword_1)))
-
-This is not the most efficient form of SQL, so while association proxies can be
-convenient for generating WHERE criteria quickly, SQL results should be
-inspected and "unrolled" into explicit JOIN criteria for best use, especially
-when chaining association proxies together.
-
-
-.. versionchanged:: 1.3 Association proxy features distinct querying modes
- based on the type of target. See :ref:`change_4351`.
-
-
-
-.. _cascade_scalar_deletes:
-
-Cascading Scalar Deletes
-------------------------
-
-.. versionadded:: 1.3
-
-Given a mapping as::
-
- from __future__ import annotations
- from sqlalchemy import Column, ForeignKey, Integer, String
- from sqlalchemy.ext.associationproxy import association_proxy, AssociationProxy
- from sqlalchemy.orm import DeclarativeBase, relationship
- from sqlalchemy.orm.collections import attribute_keyed_dict
- from sqlalchemy.orm.collections import Mapped
-
-
- class Base(DeclarativeBase):
- pass
-
-
- class A(Base):
- __tablename__ = "test_a"
- id: Mapped[int] = mapped_column(primary_key=True)
- ab: Mapped[AB] = relationship(uselist=False)
- b: AssociationProxy[B] = association_proxy(
- "ab", "b", creator=lambda b: AB(b=b), cascade_scalar_deletes=True
- )
-
-
- class B(Base):
- __tablename__ = "test_b"
- id: Mapped[int] = mapped_column(primary_key=True)
-
-
- class AB(Base):
- __tablename__ = "test_ab"
- a_id: Mapped[int] = mapped_column(ForeignKey(A.id), primary_key=True)
- b_id: Mapped[int] = mapped_column(ForeignKey(B.id), primary_key=True)
-
- b: Mapped[B] = relationship()
-
-An assignment to ``A.b`` will generate an ``AB`` object::
-
- a.b = B()
-
-The ``A.b`` association is scalar, and includes use of the parameter
-:paramref:`.AssociationProxy.cascade_scalar_deletes`. When this parameter
-is enabled, setting ``A.b``
-to ``None`` will remove ``A.ab`` as well::
-
- a.b = None
- assert a.ab is None
-
-When :paramref:`.AssociationProxy.cascade_scalar_deletes` is not set,
-the association object ``a.ab`` above would remain in place.
-
-Note that this is not the behavior for collection-based association proxies;
-in that case, the intermediary association object is always removed when
-members of the proxied collection are removed. Whether or not the row is
-deleted depends on the relationship cascade setting.
-
-.. seealso::
-
- :ref:`unitofwork_cascades`
-
-API Documentation
------------------
-
-.. autofunction:: association_proxy
-
-.. autoclass:: AssociationProxy
- :members:
- :undoc-members:
- :inherited-members:
-
-.. autoclass:: AssociationProxyInstance
- :members:
- :undoc-members:
- :inherited-members:
-
-.. autoclass:: ObjectAssociationProxyInstance
- :members:
- :inherited-members:
-
-.. autoclass:: ColumnAssociationProxyInstance
- :members:
- :inherited-members:
-
-.. autoclass:: AssociationProxyExtensionType
- :members:
+++ /dev/null
-.. _asyncio_toplevel:
-
-Asynchronous I/O (asyncio)
-==========================
-
-Support for Python asyncio. Support for Core and ORM usage is
-included, using asyncio-compatible dialects.
-
-.. versionadded:: 1.4
-
-.. warning:: Please read :ref:`asyncio_install` for important platform
- installation notes for many platforms, including **Apple M1 Architecture**.
-
-.. seealso::
-
- :ref:`change_3414` - initial feature announcement
-
- :ref:`examples_asyncio` - example scripts illustrating working examples
- of Core and ORM use within the asyncio extension.
-
-.. _asyncio_install:
-
-Asyncio Platform Installation Notes (Including Apple M1)
----------------------------------------------------------
-
-The asyncio extension requires Python 3 only. It also depends
-upon the `greenlet <https://pypi.org/project/greenlet/>`_ library. This
-dependency is installed by default on common machine platforms including:
-
-.. sourcecode:: text
-
- x86_64 aarch64 ppc64le amd64 win32
-
-For the above platforms, ``greenlet`` is known to supply pre-built wheel files.
-For other platforms, **greenlet does not install by default**;
-the current file listing for greenlet can be seen at
-`Greenlet - Download Files <https://pypi.org/project/greenlet/#files>`_.
-Note that **there are many architectures omitted, including Apple M1**.
-
-To install SQLAlchemy while ensuring the ``greenlet`` dependency is present
-regardless of what platform is in use, the
-``[asyncio]`` `setuptools extra <https://packaging.python.org/en/latest/tutorials/installing-packages/#installing-setuptools-extras>`_
-may be installed
-as follows, which will include also instruct ``pip`` to install ``greenlet``:
-
-.. sourcecode:: text
-
- pip install sqlalchemy[asyncio]
-
-Note that installation of ``greenlet`` on platforms that do not have a pre-built
-wheel file means that ``greenlet`` will be built from source, which requires
-that Python's development libraries also be present.
-
-
-Synopsis - Core
----------------
-
-For Core use, the :func:`_asyncio.create_async_engine` function creates an
-instance of :class:`_asyncio.AsyncEngine` which then offers an async version of
-the traditional :class:`_engine.Engine` API. The
-:class:`_asyncio.AsyncEngine` delivers an :class:`_asyncio.AsyncConnection` via
-its :meth:`_asyncio.AsyncEngine.connect` and :meth:`_asyncio.AsyncEngine.begin`
-methods which both deliver asynchronous context managers. The
-:class:`_asyncio.AsyncConnection` can then invoke statements using either the
-:meth:`_asyncio.AsyncConnection.execute` method to deliver a buffered
-:class:`_engine.Result`, or the :meth:`_asyncio.AsyncConnection.stream` method
-to deliver a streaming server-side :class:`_asyncio.AsyncResult`::
-
- import asyncio
-
- from sqlalchemy import Column
- from sqlalchemy import MetaData
- from sqlalchemy import select
- from sqlalchemy import String
- from sqlalchemy import Table
- from sqlalchemy.ext.asyncio import create_async_engine
-
- meta = MetaData()
- t1 = Table("t1", meta, Column("name", String(50), primary_key=True))
-
-
- async def async_main() -> None:
- engine = create_async_engine(
- "postgresql+asyncpg://scott:tiger@localhost/test",
- echo=True,
- )
-
- async with engine.begin() as conn:
- await conn.run_sync(meta.create_all)
-
- await conn.execute(
- t1.insert(), [{"name": "some name 1"}, {"name": "some name 2"}]
- )
-
- async with engine.connect() as conn:
- # select a Result, which will be delivered with buffered
- # results
- result = await conn.execute(select(t1).where(t1.c.name == "some name 1"))
-
- print(result.fetchall())
-
- # for AsyncEngine created in function scope, close and
- # clean-up pooled connections
- await engine.dispose()
-
-
- asyncio.run(async_main())
-
-Above, the :meth:`_asyncio.AsyncConnection.run_sync` method may be used to
-invoke special DDL functions such as :meth:`_schema.MetaData.create_all` that
-don't include an awaitable hook.
-
-.. tip:: It's advisable to invoke the :meth:`_asyncio.AsyncEngine.dispose` method
- using ``await`` when using the :class:`_asyncio.AsyncEngine` object in a
- scope that will go out of context and be garbage collected, as illustrated in the
- ``async_main`` function in the above example. This ensures that any
- connections held open by the connection pool will be properly disposed
- within an awaitable context. Unlike when using blocking IO, SQLAlchemy
- cannot properly dispose of these connections within methods like ``__del__``
- or weakref finalizers as there is no opportunity to invoke ``await``.
- Failing to explicitly dispose of the engine when it falls out of scope
- may result in warnings emitted to standard out resembling the form
- ``RuntimeError: Event loop is closed`` within garbage collection.
-
-The :class:`_asyncio.AsyncConnection` also features a "streaming" API via
-the :meth:`_asyncio.AsyncConnection.stream` method that returns an
-:class:`_asyncio.AsyncResult` object. This result object uses a server-side
-cursor and provides an async/await API, such as an async iterator::
-
- async with engine.connect() as conn:
- async_result = await conn.stream(select(t1))
-
- async for row in async_result:
- print("row: %s" % (row,))
-
-.. _asyncio_orm:
-
-
-Synopsis - ORM
----------------
-
-Using :term:`2.0 style` querying, the :class:`_asyncio.AsyncSession` class
-provides full ORM functionality. Within the default mode of use, special care
-must be taken to avoid :term:`lazy loading` or other expired-attribute access
-involving ORM relationships and column attributes; the next
-section :ref:`asyncio_orm_avoid_lazyloads` details this. The example below
-illustrates a complete example including mapper and session configuration::
-
- from __future__ import annotations
-
- import asyncio
- import datetime
- from typing import List
-
- from sqlalchemy import ForeignKey
- from sqlalchemy import func
- from sqlalchemy import select
- from sqlalchemy.ext.asyncio import async_sessionmaker
- from sqlalchemy.ext.asyncio import AsyncSession
- from sqlalchemy.ext.asyncio import create_async_engine
- from sqlalchemy.orm import DeclarativeBase
- from sqlalchemy.orm import Mapped
- from sqlalchemy.orm import mapped_column
- from sqlalchemy.orm import relationship
- from sqlalchemy.orm import selectinload
-
-
- class Base(DeclarativeBase):
- pass
-
-
- class A(Base):
- __tablename__ = "a"
-
- id: Mapped[int] = mapped_column(primary_key=True)
- data: Mapped[str]
- create_date: Mapped[datetime.datetime] = mapped_column(server_default=func.now())
- bs: Mapped[List[B]] = relationship(lazy="raise")
-
-
- class B(Base):
- __tablename__ = "b"
- id: Mapped[int] = mapped_column(primary_key=True)
- a_id: Mapped[int] = mapped_column(ForeignKey("a.id"))
- data: Mapped[str]
-
-
- async def insert_objects(async_session: async_sessionmaker[AsyncSession]) -> None:
-
- async with async_session() as session:
- async with session.begin():
- session.add_all(
- [
- A(bs=[B(), B()], data="a1"),
- A(bs=[], data="a2"),
- A(bs=[B(), B()], data="a3"),
- ]
- )
-
-
- async def select_and_update_objects(
- async_session: async_sessionmaker[AsyncSession],
- ) -> None:
-
- async with async_session() as session:
- stmt = select(A).options(selectinload(A.bs))
-
- result = await session.execute(stmt)
-
- for a1 in result.scalars():
- print(a1)
- print(f"created at: {a1.create_date}")
- for b1 in a1.bs:
- print(b1)
-
- result = await session.execute(select(A).order_by(A.id).limit(1))
-
- a1 = result.scalars().one()
-
- a1.data = "new data"
-
- await session.commit()
-
- # access attribute subsequent to commit; this is what
- # expire_on_commit=False allows
- print(a1.data)
-
-
- async def async_main() -> None:
- engine = create_async_engine(
- "postgresql+asyncpg://scott:tiger@localhost/test",
- echo=True,
- )
-
- # async_sessionmaker: a factory for new AsyncSession objects.
- # expire_on_commit - don't expire objects after transaction commit
- async_session = async_sessionmaker(engine, expire_on_commit=False)
-
- async with engine.begin() as conn:
- await conn.run_sync(Base.metadata.create_all)
-
- await insert_objects(async_session)
- await select_and_update_objects(async_session)
-
- # for AsyncEngine created in function scope, close and
- # clean-up pooled connections
- await engine.dispose()
-
-
- asyncio.run(async_main())
-
-In the example above, the :class:`_asyncio.AsyncSession` is instantiated using
-the optional :class:`_asyncio.async_sessionmaker` helper, which provides
-a factory for new :class:`_asyncio.AsyncSession` objects with a fixed set
-of parameters, which here includes associating it with
-an :class:`_asyncio.AsyncEngine` against particular database URL. It is then
-passed to other methods where it may be used in a Python asynchronous context
-manager (i.e. ``async with:`` statement) so that it is automatically closed at
-the end of the block; this is equivalent to calling the
-:meth:`_asyncio.AsyncSession.close` method.
-
-
-.. _asyncio_orm_avoid_lazyloads:
-
-Preventing Implicit IO when Using AsyncSession
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Using traditional asyncio, the application needs to avoid any points at which
-IO-on-attribute access may occur. Techniques that can be used to help
-this are below, many of which are illustrated in the preceding example.
-
-* Collections can be replaced with **write only collections** that will never
- emit IO implicitly, by using the :ref:`write_only_relationship` feature in
- SQLAlchemy 2.0. Using this feature, collections are never read from, only
- queried using explicit SQL calls. See the example
- ``async_orm_writeonly.py`` in the :ref:`examples_asyncio` section for
- an example of write-only collections used with asyncio.
-
- When using write only collections, the program's behavior is simple and easy
- to predict regarding collections. However, the downside is that there is not
- any built-in system for loading many of these collections all at once, which
- instead would need to be performed manually. Therefore, many of the
- bullets below address specific techniques when using traditional lazy-loaded
- relationships with asyncio, which requires more care.
-
-* If using traditional ORM relationships which are subject to lazy loading,
- relationships can be declared with ``lazy="raise"`` so that by
- default they will not attempt to emit SQL. In order to load collections,
- :term:`eager loading` must be used in all cases.
-
-* The most useful eager loading strategy is the
- :func:`_orm.selectinload` eager loader, which is employed in the previous
- example in order to eagerly
- load the ``A.bs`` collection within the scope of the
- ``await session.execute()`` call::
-
- stmt = select(A).options(selectinload(A.bs))
-
-* When constructing new objects, **collections are always assigned a default,
- empty collection**, such as a list in the above example::
-
- A(bs=[], data="a2")
-
- This allows the ``.bs`` collection on the above ``A`` object to be present and
- readable when the ``A`` object is flushed; otherwise, when the ``A`` is
- flushed, ``.bs`` would be unloaded and would raise an error on access.
-
-* The :class:`_asyncio.AsyncSession` is configured using
- :paramref:`_orm.Session.expire_on_commit` set to False, so that we may access
- attributes on an object subsequent to a call to
- :meth:`_asyncio.AsyncSession.commit`, as in the line at the end where we
- access an attribute::
-
- # create AsyncSession with expire_on_commit=False
- async_session = AsyncSession(engine, expire_on_commit=False)
-
- # sessionmaker version
- async_session = async_sessionmaker(engine, expire_on_commit=False)
-
- async with async_session() as session:
- result = await session.execute(select(A).order_by(A.id))
-
- a1 = result.scalars().first()
-
- # commit would normally expire all attributes
- await session.commit()
-
- # access attribute subsequent to commit; this is what
- # expire_on_commit=False allows
- print(a1.data)
-
-Other guidelines include:
-
-* Methods like :meth:`_asyncio.AsyncSession.expire` should be avoided in favor of
- :meth:`_asyncio.AsyncSession.refresh`; **if** expiration is absolutely needed.
- Expiration should generally **not** be needed as
- :paramref:`_orm.Session.expire_on_commit`
- should normally be set to ``False`` when using asyncio.
-
-* A lazy-loaded relationship **can be loaded explicitly under asyncio** using
- :meth:`_asyncio.AsyncSession.refresh`, **if** the desired attribute name
- is passed explicitly to
- :paramref:`_orm.Session.refresh.attribute_names`, e.g.::
-
- # assume a_obj is an A that has lazy loaded A.bs collection
- a_obj = await async_session.get(A, [1])
-
- # force the collection to load by naming it in attribute_names
- await async_session.refresh(a_obj, ["bs"])
-
- # collection is present
- print(f"bs collection: {a_obj.bs}")
-
- It's of course preferable to use eager loading up front in order to have
- collections already set up without the need to lazy-load.
-
- .. versionadded:: 2.0.4 Added support for
- :meth:`_asyncio.AsyncSession.refresh` and the underlying
- :meth:`_orm.Session.refresh` method to force lazy-loaded relationships
- to load, if they are named explicitly in the
- :paramref:`_orm.Session.refresh.attribute_names` parameter.
- In previous versions, the relationship would be silently skipped even
- if named in the parameter.
-
-* Avoid using the ``all`` cascade option documented at :ref:`unitofwork_cascades`
- in favor of listing out the desired cascade features explicitly. The
- ``all`` cascade option implies among others the :ref:`cascade_refresh_expire`
- setting, which means that the :meth:`.AsyncSession.refresh` method will
- expire the attributes on related objects, but not necessarily refresh those
- related objects assuming eager loading is not configured within the
- :func:`_orm.relationship`, leaving them in an expired state.
-
-* Appropriate loader options should be employed for :func:`_orm.deferred`
- columns, if used at all, in addition to that of :func:`_orm.relationship`
- constructs as noted above. See :ref:`orm_queryguide_column_deferral` for
- background on deferred column loading.
-
-.. _dynamic_asyncio:
-
-* The "dynamic" relationship loader strategy described at
- :ref:`dynamic_relationship` is not compatible by default with the asyncio approach.
- It can be used directly only if invoked within the
- :meth:`_asyncio.AsyncSession.run_sync` method described at
- :ref:`session_run_sync`, or by using its ``.statement`` attribute
- to obtain a normal select::
-
- user = await session.get(User, 42)
- addresses = (await session.scalars(user.addresses.statement)).all()
- stmt = user.addresses.statement.where(Address.email_address.startswith("patrick"))
- addresses_filter = (await session.scalars(stmt)).all()
-
- The :ref:`write only <write_only_relationship>` technique, introduced in
- version 2.0 of SQLAlchemy, is fully compatible with asyncio and should be
- preferred.
-
- .. seealso::
-
- :ref:`migration_20_dynamic_loaders` - notes on migration to 2.0 style
-
-* If using asyncio with a database that does not support RETURNING, such as
- MySQL 8, server default values such as generated timestamps will not be
- available on newly flushed objects unless the
- :paramref:`_orm.Mapper.eager_defaults` option is used. In SQLAlchemy 2.0,
- this behavior is applied automatically to backends like PostgreSQL, SQLite
- and MariaDB which use RETURNING to fetch new values when rows are
- INSERTed.
-
-.. _session_run_sync:
-
-Running Synchronous Methods and Functions under asyncio
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. deepalchemy:: This approach is essentially exposing publicly the
- mechanism by which SQLAlchemy is able to provide the asyncio interface
- in the first place. While there is no technical issue with doing so, overall
- the approach can probably be considered "controversial" as it works against
- some of the central philosophies of the asyncio programming model, which
- is essentially that any programming statement that can potentially result
- in IO being invoked **must** have an ``await`` call, lest the program
- does not make it explicitly clear every line at which IO may occur.
- This approach does not change that general idea, except that it allows
- a series of synchronous IO instructions to be exempted from this rule
- within the scope of a function call, essentially bundled up into a single
- awaitable.
-
-As an alternative means of integrating traditional SQLAlchemy "lazy loading"
-within an asyncio event loop, an **optional** method known as
-:meth:`_asyncio.AsyncSession.run_sync` is provided which will run any
-Python function inside of a greenlet, where traditional synchronous
-programming concepts will be translated to use ``await`` when they reach the
-database driver. A hypothetical approach here is an asyncio-oriented
-application can package up database-related methods into functions that are
-invoked using :meth:`_asyncio.AsyncSession.run_sync`.
-
-Altering the above example, if we didn't use :func:`_orm.selectinload`
-for the ``A.bs`` collection, we could accomplish our treatment of these
-attribute accesses within a separate function::
-
- import asyncio
-
- from sqlalchemy import select
- from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
-
-
- def fetch_and_update_objects(session):
- """run traditional sync-style ORM code in a function that will be
- invoked within an awaitable.
-
- """
-
- # the session object here is a traditional ORM Session.
- # all features are available here including legacy Query use.
-
- stmt = select(A)
-
- result = session.execute(stmt)
- for a1 in result.scalars():
- print(a1)
-
- # lazy loads
- for b1 in a1.bs:
- print(b1)
-
- # legacy Query use
- a1 = session.query(A).order_by(A.id).first()
-
- a1.data = "new data"
-
-
- async def async_main():
- engine = create_async_engine(
- "postgresql+asyncpg://scott:tiger@localhost/test",
- echo=True,
- )
- async with engine.begin() as conn:
- await conn.run_sync(Base.metadata.drop_all)
- await conn.run_sync(Base.metadata.create_all)
-
- async with AsyncSession(engine) as session:
- async with session.begin():
- session.add_all(
- [
- A(bs=[B(), B()], data="a1"),
- A(bs=[B()], data="a2"),
- A(bs=[B(), B()], data="a3"),
- ]
- )
-
- await session.run_sync(fetch_and_update_objects)
-
- await session.commit()
-
- # for AsyncEngine created in function scope, close and
- # clean-up pooled connections
- await engine.dispose()
-
-
- asyncio.run(async_main())
-
-The above approach of running certain functions within a "sync" runner
-has some parallels to an application that runs a SQLAlchemy application
-on top of an event-based programming library such as ``gevent``. The
-differences are as follows:
-
-1. unlike when using ``gevent``, we can continue to use the standard Python
- asyncio event loop, or any custom event loop, without the need to integrate
- into the ``gevent`` event loop.
-
-2. There is no "monkeypatching" whatsoever. The above example makes use of
- a real asyncio driver and the underlying SQLAlchemy connection pool is also
- using the Python built-in ``asyncio.Queue`` for pooling connections.
-
-3. The program can freely switch between async/await code and contained
- functions that use sync code with virtually no performance penalty. There
- is no "thread executor" or any additional waiters or synchronization in use.
-
-4. The underlying network drivers are also using pure Python asyncio
- concepts, no third party networking libraries as ``gevent`` and ``eventlet``
- provides are in use.
-
-.. _asyncio_events:
-
-Using events with the asyncio extension
----------------------------------------
-
-The SQLAlchemy :ref:`event system <event_toplevel>` is not directly exposed
-by the asyncio extension, meaning there is not yet an "async" version of a
-SQLAlchemy event handler.
-
-However, as the asyncio extension surrounds the usual synchronous SQLAlchemy
-API, regular "synchronous" style event handlers are freely available as they
-would be if asyncio were not used.
-
-As detailed below, there are two current strategies to register events given
-asyncio-facing APIs:
-
-* Events can be registered at the instance level (e.g. a specific
- :class:`_asyncio.AsyncEngine` instance) by associating the event with the
- ``sync`` attribute that refers to the proxied object. For example to register
- the :meth:`_events.PoolEvents.connect` event against an
- :class:`_asyncio.AsyncEngine` instance, use its
- :attr:`_asyncio.AsyncEngine.sync_engine` attribute as target. Targets
- include:
-
- :attr:`_asyncio.AsyncEngine.sync_engine`
-
- :attr:`_asyncio.AsyncConnection.sync_connection`
-
- :attr:`_asyncio.AsyncConnection.sync_engine`
-
- :attr:`_asyncio.AsyncSession.sync_session`
-
-* To register an event at the class level, targeting all instances of the same type (e.g.
- all :class:`_asyncio.AsyncSession` instances), use the corresponding
- sync-style class. For example to register the
- :meth:`_ormevents.SessionEvents.before_commit` event against the
- :class:`_asyncio.AsyncSession` class, use the :class:`_orm.Session` class as
- the target.
-
-* To register at the :class:`_orm.sessionmaker` level, combine an explicit
- :class:`_orm.sessionmaker` with an :class:`_asyncio.async_sessionmaker`
- using :paramref:`_asyncio.async_sessionmaker.sync_session_class`, and
- associate events with the :class:`_orm.sessionmaker`.
-
-When working within an event handler that is within an asyncio context, objects
-like the :class:`_engine.Connection` continue to work in their usual
-"synchronous" way without requiring ``await`` or ``async`` usage; when messages
-are ultimately received by the asyncio database adapter, the calling style is
-transparently adapted back into the asyncio calling style. For events that
-are passed a DBAPI level connection, such as :meth:`_events.PoolEvents.connect`,
-the object is a :term:`pep-249` compliant "connection" object which will adapt
-sync-style calls into the asyncio driver.
-
-Examples of Event Listeners with Async Engines / Sessions / Sessionmakers
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Some examples of sync style event handlers associated with async-facing API
-constructs are illustrated below:
-
-* **Core Events on AsyncEngine**
-
- In this example, we access the :attr:`_asyncio.AsyncEngine.sync_engine`
- attribute of :class:`_asyncio.AsyncEngine` as the target for
- :class:`.ConnectionEvents` and :class:`.PoolEvents`::
-
- import asyncio
-
- from sqlalchemy import event
- from sqlalchemy import text
- from sqlalchemy.engine import Engine
- from sqlalchemy.ext.asyncio import create_async_engine
-
- engine = create_async_engine("postgresql+asyncpg://scott:tiger@localhost:5432/test")
-
-
- # connect event on instance of Engine
- @event.listens_for(engine.sync_engine, "connect")
- def my_on_connect(dbapi_con, connection_record):
- print("New DBAPI connection:", dbapi_con)
- cursor = dbapi_con.cursor()
-
- # sync style API use for adapted DBAPI connection / cursor
- cursor.execute("select 'execute from event'")
- print(cursor.fetchone()[0])
-
-
- # before_execute event on all Engine instances
- @event.listens_for(Engine, "before_execute")
- def my_before_execute(
- conn,
- clauseelement,
- multiparams,
- params,
- execution_options,
- ):
- print("before execute!")
-
-
- async def go():
- async with engine.connect() as conn:
- await conn.execute(text("select 1"))
- await engine.dispose()
-
-
- asyncio.run(go())
-
- Output:
-
- .. sourcecode:: text
-
- New DBAPI connection: <AdaptedConnection <asyncpg.connection.Connection object at 0x7f33f9b16960>>
- execute from event
- before execute!
-
-
-* **ORM Events on AsyncSession**
-
- In this example, we access :attr:`_asyncio.AsyncSession.sync_session` as the
- target for :class:`_orm.SessionEvents`::
-
- import asyncio
-
- from sqlalchemy import event
- from sqlalchemy import text
- from sqlalchemy.ext.asyncio import AsyncSession
- from sqlalchemy.ext.asyncio import create_async_engine
- from sqlalchemy.orm import Session
-
- engine = create_async_engine("postgresql+asyncpg://scott:tiger@localhost:5432/test")
-
- session = AsyncSession(engine)
-
-
- # before_commit event on instance of Session
- @event.listens_for(session.sync_session, "before_commit")
- def my_before_commit(session):
- print("before commit!")
-
- # sync style API use on Session
- connection = session.connection()
-
- # sync style API use on Connection
- result = connection.execute(text("select 'execute from event'"))
- print(result.first())
-
-
- # after_commit event on all Session instances
- @event.listens_for(Session, "after_commit")
- def my_after_commit(session):
- print("after commit!")
-
-
- async def go():
- await session.execute(text("select 1"))
- await session.commit()
-
- await session.close()
- await engine.dispose()
-
-
- asyncio.run(go())
-
- Output:
-
- .. sourcecode:: text
-
- before commit!
- execute from event
- after commit!
-
-
-* **ORM Events on async_sessionmaker**
-
- For this use case, we make a :class:`_orm.sessionmaker` as the event target,
- then assign it to the :class:`_asyncio.async_sessionmaker` using
- the :paramref:`_asyncio.async_sessionmaker.sync_session_class` parameter::
-
- import asyncio
-
- from sqlalchemy import event
- from sqlalchemy.ext.asyncio import async_sessionmaker
- from sqlalchemy.orm import sessionmaker
-
- sync_maker = sessionmaker()
- maker = async_sessionmaker(sync_session_class=sync_maker)
-
-
- @event.listens_for(sync_maker, "before_commit")
- def before_commit(session):
- print("before commit")
-
-
- async def main():
- async_session = maker()
-
- await async_session.commit()
-
-
- asyncio.run(main())
-
- Output:
-
- .. sourcecode:: text
-
- before commit
-
-
-.. topic:: asyncio and events, two opposites
-
- SQLAlchemy events by their nature take place within the **interior** of a
- particular SQLAlchemy process; that is, an event always occurs *after* some
- particular SQLAlchemy API has been invoked by end-user code, and *before*
- some other internal aspect of that API occurs.
-
- Contrast this to the architecture of the asyncio extension, which takes
- place on the **exterior** of SQLAlchemy's usual flow from end-user API to
- DBAPI function.
-
- The flow of messaging may be visualized as follows:
-
- .. sourcecode:: text
-
- SQLAlchemy SQLAlchemy SQLAlchemy SQLAlchemy plain
- asyncio asyncio ORM/Core asyncio asyncio
- (public (internal) (internal)
- facing)
- -------------|------------|------------------------|-----------|------------
- asyncio API | | | |
- call -> | | | |
- | -> -> | | -> -> |
- |~~~~~~~~~~~~| sync API call -> |~~~~~~~~~~~|
- | asyncio | event hooks -> | sync |
- | to | invoke action -> | to |
- | sync | event hooks -> | asyncio |
- | (greenlet) | dialect -> | (leave |
- |~~~~~~~~~~~~| event hooks -> | greenlet) |
- | -> -> | sync adapted |~~~~~~~~~~~|
- | | DBAPI -> | -> -> | asyncio
- | | | | driver -> database
-
-
- Where above, an API call always starts as asyncio, flows through the
- synchronous API, and ends as asyncio, before results are propagated through
- this same chain in the opposite direction. In between, the message is
- adapted first into sync-style API use, and then back out to async style.
- Event hooks then by their nature occur in the middle of the "sync-style API
- use". From this it follows that the API presented within event hooks
- occurs inside the process by which asyncio API requests have been adapted
- to sync, and outgoing messages to the database API will be converted
- to asyncio transparently.
-
-.. _asyncio_events_run_async:
-
-Using awaitable-only driver methods in connection pool and other events
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-As discussed in the above section, event handlers such as those oriented
-around the :class:`.PoolEvents` event handlers receive a sync-style "DBAPI" connection,
-which is a wrapper object supplied by SQLAlchemy asyncio dialects to adapt
-the underlying asyncio "driver" connection into one that can be used by
-SQLAlchemy's internals. A special use case arises when the user-defined
-implementation for such an event handler needs to make use of the
-ultimate "driver" connection directly, using awaitable only methods on that
-driver connection. One such example is the ``.set_type_codec()`` method
-supplied by the asyncpg driver.
-
-To accommodate this use case, SQLAlchemy's :class:`.AdaptedConnection`
-class provides a method :meth:`.AdaptedConnection.run_async` that allows
-an awaitable function to be invoked within the "synchronous" context of
-an event handler or other SQLAlchemy internal. This method is directly
-analogous to the :meth:`_asyncio.AsyncConnection.run_sync` method that
-allows a sync-style method to run under async.
-
-:meth:`.AdaptedConnection.run_async` should be passed a function that will
-accept the innermost "driver" connection as a single argument, and return
-an awaitable that will be invoked by the :meth:`.AdaptedConnection.run_async`
-method. The given function itself does not need to be declared as ``async``;
-it's perfectly fine for it to be a Python ``lambda:``, as the return awaitable
-value will be invoked after being returned::
-
- from sqlalchemy import event
- from sqlalchemy.ext.asyncio import create_async_engine
-
- engine = create_async_engine(...)
-
-
- @event.listens_for(engine.sync_engine, "connect")
- def register_custom_types(dbapi_connection, *args):
- dbapi_connection.run_async(
- lambda connection: connection.set_type_codec(
- "MyCustomType",
- encoder,
- decoder, # ...
- )
- )
-
-Above, the object passed to the ``register_custom_types`` event handler
-is an instance of :class:`.AdaptedConnection`, which provides a DBAPI-like
-interface to an underlying async-only driver-level connection object.
-The :meth:`.AdaptedConnection.run_async` method then provides access to an
-awaitable environment where the underlying driver level connection may be
-acted upon.
-
-.. versionadded:: 1.4.30
-
-
-Using multiple asyncio event loops
-----------------------------------
-
-An application that makes use of multiple event loops, for example in the
-uncommon case of combining asyncio with multithreading, should not share the
-same :class:`_asyncio.AsyncEngine` with different event loops when using the
-default pool implementation.
-
-If an :class:`_asyncio.AsyncEngine` is be passed from one event loop to another,
-the method :meth:`_asyncio.AsyncEngine.dispose()` should be called before it's
-re-used on a new event loop. Failing to do so may lead to a ``RuntimeError``
-along the lines of
-``Task <Task pending ...> got Future attached to a different loop``
-
-If the same engine must be shared between different loop, it should be configured
-to disable pooling using :class:`~sqlalchemy.pool.NullPool`, preventing the Engine
-from using any connection more than once::
-
- from sqlalchemy.ext.asyncio import create_async_engine
- from sqlalchemy.pool import NullPool
-
- engine = create_async_engine(
- "postgresql+asyncpg://user:pass@host/dbname",
- poolclass=NullPool,
- )
-
-.. _asyncio_scoped_session:
-
-Using asyncio scoped session
-----------------------------
-
-The "scoped session" pattern used in threaded SQLAlchemy with the
-:class:`.scoped_session` object is also available in asyncio, using
-an adapted version called :class:`_asyncio.async_scoped_session`.
-
-.. tip:: SQLAlchemy generally does not recommend the "scoped" pattern
- for new development as it relies upon mutable global state that must also be
- explicitly torn down when work within the thread or task is complete.
- Particularly when using asyncio, it's likely a better idea to pass the
- :class:`_asyncio.AsyncSession` directly to the awaitable functions that need
- it.
-
-When using :class:`_asyncio.async_scoped_session`, as there's no "thread-local"
-concept in the asyncio context, the "scopefunc" parameter must be provided to
-the constructor. The example below illustrates using the
-``asyncio.current_task()`` function for this purpose::
-
- from asyncio import current_task
-
- from sqlalchemy.ext.asyncio import (
- async_scoped_session,
- async_sessionmaker,
- )
-
- async_session_factory = async_sessionmaker(
- some_async_engine,
- expire_on_commit=False,
- )
- AsyncScopedSession = async_scoped_session(
- async_session_factory,
- scopefunc=current_task,
- )
- some_async_session = AsyncScopedSession()
-
-.. warning:: The "scopefunc" used by :class:`_asyncio.async_scoped_session`
- is invoked **an arbitrary number of times** within a task, once for each
- time the underlying :class:`_asyncio.AsyncSession` is accessed. The function
- should therefore be **idempotent** and lightweight, and should not attempt
- to create or mutate any state, such as establishing callbacks, etc.
-
-.. warning:: Using ``current_task()`` for the "key" in the scope requires that
- the :meth:`_asyncio.async_scoped_session.remove` method is called from
- within the outermost awaitable, to ensure the key is removed from the
- registry when the task is complete, otherwise the task handle as well as
- the :class:`_asyncio.AsyncSession` will remain in memory, essentially
- creating a memory leak. See the following example which illustrates
- the correct use of :meth:`_asyncio.async_scoped_session.remove`.
-
-:class:`_asyncio.async_scoped_session` includes **proxy
-behavior** similar to that of :class:`.scoped_session`, which means it can be
-treated as a :class:`_asyncio.AsyncSession` directly, keeping in mind that
-the usual ``await`` keywords are necessary, including for the
-:meth:`_asyncio.async_scoped_session.remove` method::
-
- async def some_function(some_async_session, some_object):
- # use the AsyncSession directly
- some_async_session.add(some_object)
-
- # use the AsyncSession via the context-local proxy
- await AsyncScopedSession.commit()
-
- # "remove" the current proxied AsyncSession for the local context
- await AsyncScopedSession.remove()
-
-.. versionadded:: 1.4.19
-
-.. currentmodule:: sqlalchemy.ext.asyncio
-
-
-.. _asyncio_inspector:
-
-Using the Inspector to inspect schema objects
----------------------------------------------------
-
-SQLAlchemy does not yet offer an asyncio version of the
-:class:`_reflection.Inspector` (introduced at :ref:`metadata_reflection_inspector`),
-however the existing interface may be used in an asyncio context by
-leveraging the :meth:`_asyncio.AsyncConnection.run_sync` method of
-:class:`_asyncio.AsyncConnection`::
-
- import asyncio
-
- from sqlalchemy import inspect
- from sqlalchemy.ext.asyncio import create_async_engine
-
- engine = create_async_engine("postgresql+asyncpg://scott:tiger@localhost/test")
-
-
- def use_inspector(conn):
- inspector = inspect(conn)
- # use the inspector
- print(inspector.get_view_names())
- # return any value to the caller
- return inspector.get_table_names()
-
-
- async def async_main():
- async with engine.connect() as conn:
- tables = await conn.run_sync(use_inspector)
-
-
- asyncio.run(async_main())
-
-.. seealso::
-
- :ref:`metadata_reflection`
-
- :ref:`inspection_toplevel`
-
-Engine API Documentation
--------------------------
-
-.. autofunction:: create_async_engine
-
-.. autofunction:: async_engine_from_config
-
-.. autofunction:: create_async_pool_from_url
-
-.. autoclass:: AsyncEngine
- :members:
-
-.. autoclass:: AsyncConnection
- :members:
-
-.. autoclass:: AsyncTransaction
- :members:
-
-Result Set API Documentation
-----------------------------------
-
-The :class:`_asyncio.AsyncResult` object is an async-adapted version of the
-:class:`_result.Result` object. It is only returned when using the
-:meth:`_asyncio.AsyncConnection.stream` or :meth:`_asyncio.AsyncSession.stream`
-methods, which return a result object that is on top of an active database
-cursor.
-
-.. autoclass:: AsyncResult
- :members:
- :inherited-members:
-
-.. autoclass:: AsyncScalarResult
- :members:
- :inherited-members:
-
-.. autoclass:: AsyncMappingResult
- :members:
- :inherited-members:
-
-.. autoclass:: AsyncTupleResult
-
-ORM Session API Documentation
------------------------------
-
-.. autofunction:: async_object_session
-
-.. autofunction:: async_session
-
-.. autoclass:: async_sessionmaker
- :members:
- :inherited-members:
-
-.. autoclass:: async_scoped_session
- :members:
- :inherited-members:
-
-.. autoclass:: AsyncSession
- :members:
- :exclude-members: sync_session_class
-
- .. autoattribute:: sync_session_class
-
-.. autoclass:: AsyncSessionTransaction
- :members:
-
-
-
+++ /dev/null
-.. _automap_toplevel:
-
-Automap
-=======
-
-.. automodule:: sqlalchemy.ext.automap
-
-API Reference
--------------
-
-.. autofunction:: automap_base
-
-.. autoclass:: AutomapBase
- :members:
-
-.. autofunction:: classname_for_table
-
-.. autofunction:: name_for_scalar_relationship
-
-.. autofunction:: name_for_collection_relationship
-
-.. autofunction:: generate_relationship
+++ /dev/null
-.. _baked_toplevel:
-
-Baked Queries
-=============
-
-.. module:: sqlalchemy.ext.baked
-
-``baked`` provides an alternative creational pattern for
-:class:`~.query.Query` objects, which allows for caching of the object's
-construction and string-compilation steps. This means that for a
-particular :class:`~.query.Query` building scenario that is used more than
-once, all of the Python function invocation involved in building the query
-from its initial construction up through generating a SQL string will only
-occur **once**, rather than for each time that query is built up and executed.
-
-The rationale for this system is to greatly reduce Python interpreter
-overhead for everything that occurs **before the SQL is emitted**.
-The caching of the "baked" system does **not** in any way reduce SQL calls or
-cache the **return results** from the database. A technique that demonstrates
-the caching of the SQL calls and result sets themselves is available in
-:ref:`examples_caching`.
-
-.. deprecated:: 1.4 SQLAlchemy 1.4 and 2.0 feature an all-new direct query
- caching system that removes the need for the :class:`.BakedQuery` system.
- Caching is now transparently active for all Core and ORM queries with no
- action taken by the user, using the system described at :ref:`sql_caching`.
-
-
-.. deepalchemy::
-
- The :mod:`sqlalchemy.ext.baked` extension is **not for beginners**. Using
- it correctly requires a good high level understanding of how SQLAlchemy, the
- database driver, and the backend database interact with each other. This
- extension presents a very specific kind of optimization that is not ordinarily
- needed. As noted above, it **does not cache queries**, only the string
- formulation of the SQL itself.
-
-Synopsis
---------
-
-Usage of the baked system starts by producing a so-called "bakery", which
-represents storage for a particular series of query objects::
-
- from sqlalchemy.ext import baked
-
- bakery = baked.bakery()
-
-The above "bakery" will store cached data in an LRU cache that defaults
-to 200 elements, noting that an ORM query will typically contain one entry
-for the ORM query as invoked, as well as one entry per database dialect for
-the SQL string.
-
-The bakery allows us to build up a :class:`~.query.Query` object by specifying
-its construction as a series of Python callables, which are typically lambdas.
-For succinct usage, it overrides the ``+=`` operator so that a typical
-query build-up looks like the following::
-
- from sqlalchemy import bindparam
-
-
- def search_for_user(session, username, email=None):
- baked_query = bakery(lambda session: session.query(User))
- baked_query += lambda q: q.filter(User.name == bindparam("username"))
-
- baked_query += lambda q: q.order_by(User.id)
-
- if email:
- baked_query += lambda q: q.filter(User.email == bindparam("email"))
-
- result = baked_query(session).params(username=username, email=email).all()
-
- return result
-
-Following are some observations about the above code:
-
-1. The ``baked_query`` object is an instance of :class:`.BakedQuery`. This
- object is essentially the "builder" for a real orm :class:`~.query.Query`
- object, but it is not itself the *actual* :class:`~.query.Query`
- object.
-
-2. The actual :class:`~.query.Query` object is not built at all, until the
- very end of the function when :meth:`_baked.Result.all` is called.
-
-3. The steps that are added to the ``baked_query`` object are all expressed
- as Python functions, typically lambdas. The first lambda given
- to the :func:`.bakery` function receives a :class:`.Session` as its
- argument. The remaining lambdas each receive a :class:`~.query.Query`
- as their argument.
-
-4. In the above code, even though our application may call upon
- ``search_for_user()`` many times, and even though within each invocation
- we build up an entirely new :class:`.BakedQuery` object,
- *all of the lambdas are only called once*. Each lambda is **never** called
- a second time for as long as this query is cached in the bakery.
-
-5. The caching is achieved by storing references to the **lambda objects
- themselves** in order to formulate a cache key; that is, the fact that the
- Python interpreter assigns an in-Python identity to these functions is
- what determines how to identify the query on successive runs. For
- those invocations of ``search_for_user()`` where the ``email`` parameter
- is specified, the callable ``lambda q: q.filter(User.email == bindparam('email'))``
- will be part of the cache key that's retrieved; when ``email`` is
- ``None``, this callable is not part of the cache key.
-
-6. Because the lambdas are all called only once, it is essential that no
- variables which may change across calls are referenced **within** the
- lambdas; instead, assuming these are values to be bound into the
- SQL string, we use :func:`.bindparam` to construct named parameters,
- where we apply their actual values later using :meth:`_baked.Result.params`.
-
-
-Performance
------------
-
-The baked query probably looks a little odd, a little bit awkward and
-a little bit verbose. However, the savings in
-Python performance for a query which is invoked lots of times in an
-application are very dramatic. The example suite ``short_selects``
-demonstrated in :ref:`examples_performance` illustrates a comparison
-of queries which each return only one row, such as the following regular
-query::
-
- session = Session(bind=engine)
- for id_ in random.sample(ids, n):
- session.query(Customer).filter(Customer.id == id_).one()
-
-compared to the equivalent "baked" query::
-
- bakery = baked.bakery()
- s = Session(bind=engine)
- for id_ in random.sample(ids, n):
- q = bakery(lambda s: s.query(Customer))
- q += lambda q: q.filter(Customer.id == bindparam("id"))
- q(s).params(id=id_).one()
-
-The difference in Python function call count for an iteration of 10000
-calls to each block are:
-
-.. sourcecode:: text
-
- test_baked_query : test a baked query of the full entity.
- (10000 iterations); total fn calls 1951294
-
- test_orm_query : test a straight ORM query of the full entity.
- (10000 iterations); total fn calls 7900535
-
-In terms of number of seconds on a powerful laptop, this comes out as:
-
-.. sourcecode:: text
-
- test_baked_query : test a baked query of the full entity.
- (10000 iterations); total time 2.174126 sec
-
- test_orm_query : test a straight ORM query of the full entity.
- (10000 iterations); total time 7.958516 sec
-
-Note that this test very intentionally features queries that only return one row.
-For queries that return many rows, the performance advantage of the baked query will have
-less and less of an impact, proportional to the time spent fetching rows.
-It is critical to keep in mind that the **baked query feature only applies to
-building the query itself, not the fetching of results**. Using the
-baked feature is by no means a guarantee to a much faster application; it is
-only a potentially useful feature for those applications that have been measured
-as being impacted by this particular form of overhead.
-
-.. topic:: Measure twice, cut once
-
- For background on how to profile a SQLAlchemy application, please see
- the section :ref:`faq_performance`. It is essential that performance
- measurement techniques are used when attempting to improve the performance
- of an application.
-
-Rationale
----------
-
-The "lambda" approach above is a superset of what would be a more
-traditional "parameterized" approach. Suppose we wished to build
-a simple system where we build a :class:`~.query.Query` just once, then
-store it in a dictionary for re-use. This is possible right now by
-just building up the query, and removing its :class:`.Session` by calling
-``my_cached_query = query.with_session(None)``::
-
- my_simple_cache = {}
-
-
- def lookup(session, id_argument):
- if "my_key" not in my_simple_cache:
- query = session.query(Model).filter(Model.id == bindparam("id"))
- my_simple_cache["my_key"] = query.with_session(None)
- else:
- query = my_simple_cache["my_key"].with_session(session)
-
- return query.params(id=id_argument).all()
-
-The above approach gets us a very minimal performance benefit.
-By re-using a :class:`~.query.Query`, we save on the Python work within
-the ``session.query(Model)`` constructor as well as calling upon
-``filter(Model.id == bindparam('id'))``, which will skip for us the building
-up of the Core expression as well as sending it to :meth:`_query.Query.filter`.
-However, the approach still regenerates the full :class:`_expression.Select`
-object every time when :meth:`_query.Query.all` is called and additionally this
-brand new :class:`_expression.Select` is sent off to the string compilation step every
-time, which for a simple case like the above is probably about 70% of the
-overhead.
-
-To reduce the additional overhead, we need some more specialized logic,
-some way to memoize the construction of the select object and the
-construction of the SQL. There is an example of this on the wiki
-in the section `BakedQuery <https://bitbucket.org/zzzeek/sqlalchemy/wiki/UsageRecipes/BakedQuery>`_,
-a precursor to this feature, however in that system, we aren't caching
-the *construction* of the query. In order to remove all the overhead,
-we need to cache both the construction of the query as well as the SQL
-compilation. Let's assume we adapted the recipe in this way
-and made ourselves a method ``.bake()`` that pre-compiles the SQL for the
-query, producing a new object that can be invoked with minimal overhead.
-Our example becomes::
-
- my_simple_cache = {}
-
-
- def lookup(session, id_argument):
- if "my_key" not in my_simple_cache:
- query = session.query(Model).filter(Model.id == bindparam("id"))
- my_simple_cache["my_key"] = query.with_session(None).bake()
- else:
- query = my_simple_cache["my_key"].with_session(session)
-
- return query.params(id=id_argument).all()
-
-Above, we've fixed the performance situation, but we still have this
-string cache key to deal with.
-
-We can use the "bakery" approach to re-frame the above in a way that
-looks less unusual than the "building up lambdas" approach, and more like
-a simple improvement upon the simple "reuse a query" approach::
-
- bakery = baked.bakery()
-
-
- def lookup(session, id_argument):
- def create_model_query(session):
- return session.query(Model).filter(Model.id == bindparam("id"))
-
- parameterized_query = bakery.bake(create_model_query)
- return parameterized_query(session).params(id=id_argument).all()
-
-Above, we use the "baked" system in a manner that is
-very similar to the simplistic "cache a query" system. However, it
-uses two fewer lines of code, does not need to manufacture a cache key of
-"my_key", and also includes the same feature as our custom "bake" function
-that caches 100% of the Python invocation work from the
-constructor of the query, to the filter call, to the production
-of the :class:`_expression.Select` object, to the string compilation step.
-
-From the above, if we ask ourselves, "what if lookup needs to make conditional decisions
-as to the structure of the query?", this is where hopefully it becomes apparent
-why "baked" is the way it is. Instead of a parameterized query building
-off from exactly one function (which is how we thought baked might work
-originally), we can build it from *any number* of functions. Consider
-our naive example, if we needed to have an additional clause in our
-query on a conditional basis::
-
- my_simple_cache = {}
-
-
- def lookup(session, id_argument, include_frobnizzle=False):
- if include_frobnizzle:
- cache_key = "my_key_with_frobnizzle"
- else:
- cache_key = "my_key_without_frobnizzle"
-
- if cache_key not in my_simple_cache:
- query = session.query(Model).filter(Model.id == bindparam("id"))
- if include_frobnizzle:
- query = query.filter(Model.frobnizzle == True)
-
- my_simple_cache[cache_key] = query.with_session(None).bake()
- else:
- query = my_simple_cache[cache_key].with_session(session)
-
- return query.params(id=id_argument).all()
-
-Our "simple" parameterized system must now be tasked with generating
-cache keys which take into account whether or not the "include_frobnizzle"
-flag was passed, as the presence of this flag means that the generated
-SQL would be entirely different. It should be apparent that as the
-complexity of query building goes up, the task of caching these queries
-becomes burdensome very quickly. We can convert the above example
-into a direct use of "bakery" as follows::
-
-
- bakery = baked.bakery()
-
-
- def lookup(session, id_argument, include_frobnizzle=False):
- def create_model_query(session):
- return session.query(Model).filter(Model.id == bindparam("id"))
-
- parameterized_query = bakery.bake(create_model_query)
-
- if include_frobnizzle:
-
- def include_frobnizzle_in_query(query):
- return query.filter(Model.frobnizzle == True)
-
- parameterized_query = parameterized_query.with_criteria(
- include_frobnizzle_in_query
- )
-
- return parameterized_query(session).params(id=id_argument).all()
-
-Above, we again cache not just the query object but all the work it needs
-to do in order to generate SQL. We also no longer need to deal with
-making sure we generate a cache key that accurately takes into account
-all of the structural modifications we've made; this is now handled
-automatically and without the chance of mistakes.
-
-This code sample is a few lines shorter than the naive example, removes
-the need to deal with cache keys, and has the vast performance benefits
-of the full so-called "baked" feature. But
-still a little verbose! Hence we take methods like :meth:`.BakedQuery.add_criteria`
-and :meth:`.BakedQuery.with_criteria` and shorten them into operators, and
-encourage (though certainly not require!) using simple lambdas, only as a
-means to reduce verbosity::
-
- bakery = baked.bakery()
-
-
- def lookup(session, id_argument, include_frobnizzle=False):
- parameterized_query = bakery.bake(
- lambda s: s.query(Model).filter(Model.id == bindparam("id"))
- )
-
- if include_frobnizzle:
- parameterized_query += lambda q: q.filter(Model.frobnizzle == True)
-
- return parameterized_query(session).params(id=id_argument).all()
-
-Where above, the approach is simpler to implement and much more similar
-in code flow to what a non-cached querying function would look like,
-hence making code easier to port.
-
-The above description is essentially a summary of the design process used
-to arrive at the current "baked" approach. Starting from the
-"normal" approaches, the additional issues of cache key construction and
-management, removal of all redundant Python execution, and queries built up
-with conditionals needed to be addressed, leading to the final approach.
-
-Special Query Techniques
-------------------------
-
-This section will describe some techniques for specific query situations.
-
-.. _baked_in:
-
-Using IN expressions
-^^^^^^^^^^^^^^^^^^^^
-
-The :meth:`.ColumnOperators.in_` method in SQLAlchemy historically renders
-a variable set of bound parameters based on the list of items that's passed
-to the method. This doesn't work for baked queries as the length of that
-list can change on different calls. To solve this problem, the
-:paramref:`.bindparam.expanding` parameter supports a late-rendered IN
-expression that is safe to be cached inside of baked query. The actual list
-of elements is rendered at statement execution time, rather than at
-statement compilation time::
-
- bakery = baked.bakery()
-
- baked_query = bakery(lambda session: session.query(User))
- baked_query += lambda q: q.filter(User.name.in_(bindparam("username", expanding=True)))
-
- result = baked_query.with_session(session).params(username=["ed", "fred"]).all()
-
-.. seealso::
-
- :paramref:`.bindparam.expanding`
-
- :meth:`.ColumnOperators.in_`
-
-Using Subqueries
-^^^^^^^^^^^^^^^^
-
-When using :class:`_query.Query` objects, it is often needed that one :class:`_query.Query`
-object is used to generate a subquery within another. In the case where the
-:class:`_query.Query` is currently in baked form, an interim method may be used to
-retrieve the :class:`_query.Query` object, using the :meth:`.BakedQuery.to_query`
-method. This method is passed the :class:`.Session` or :class:`_query.Query` that is
-the argument to the lambda callable used to generate a particular step
-of the baked query::
-
- bakery = baked.bakery()
-
- # a baked query that will end up being used as a subquery
- my_subq = bakery(lambda s: s.query(User.id))
- my_subq += lambda q: q.filter(User.id == Address.user_id)
-
- # select a correlated subquery in the top columns list,
- # we have the "session" argument, pass that
- my_q = bakery(lambda s: s.query(Address.id, my_subq.to_query(s).as_scalar()))
-
- # use a correlated subquery in some of the criteria, we have
- # the "query" argument, pass that.
- my_q += lambda q: q.filter(my_subq.to_query(q).exists())
-
-.. versionadded:: 1.3
-
-.. _baked_with_before_compile:
-
-Using the before_compile event
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-As of SQLAlchemy 1.3.11, the use of the :meth:`.QueryEvents.before_compile`
-event against a particular :class:`_query.Query` will disallow the baked query
-system from caching the query, if the event hook returns a new :class:`_query.Query`
-object that is different from the one passed in. This is so that the
-:meth:`.QueryEvents.before_compile` hook may be invoked against a particular
-:class:`_query.Query` every time it is used, to accommodate for hooks that
-alter the query differently each time. To allow a
-:meth:`.QueryEvents.before_compile` to alter a :meth:`_query.Query` object, but
-still to allow the result to be cached, the event can be registered
-passing the ``bake_ok=True`` flag::
-
- @event.listens_for(Query, "before_compile", retval=True, bake_ok=True)
- def my_event(query):
- for desc in query.column_descriptions:
- if desc["type"] is User:
- entity = desc["entity"]
- query = query.filter(entity.deleted == False)
- return query
-
-The above strategy is appropriate for an event that will modify a
-given :class:`_query.Query` in exactly the same way every time, not dependent
-on specific parameters or external state that changes.
-
-.. versionadded:: 1.3.11 - added the "bake_ok" flag to the
- :meth:`.QueryEvents.before_compile` event and disallowed caching via
- the "baked" extension from occurring for event handlers that
- return a new :class:`_query.Query` object if this flag is not set.
-
-
-Disabling Baked Queries Session-wide
-------------------------------------
-
-The flag :paramref:`.Session.enable_baked_queries` may be set to False,
-causing all baked queries to not use the cache when used against that
-:class:`.Session`::
-
- session = Session(engine, enable_baked_queries=False)
-
-Like all session flags, it is also accepted by factory objects like
-:class:`.sessionmaker` and methods like :meth:`.sessionmaker.configure`.
-
-The immediate rationale for this flag is so that an application
-which is seeing issues potentially due to cache key conflicts from user-defined
-baked queries or other baked query issues can turn the behavior off, in
-order to identify or eliminate baked queries as the cause of an issue.
-
-.. versionadded:: 1.2
-
-Lazy Loading Integration
-------------------------
-
-.. versionchanged:: 1.4 As of SQLAlchemy 1.4, the "baked query" system is no
- longer part of the relationship loading system.
- The :ref:`native caching <sql_caching>` system is used instead.
-
-
-API Documentation
------------------
-
-.. autofunction:: bakery
-
-.. autoclass:: BakedQuery
- :members:
-
-.. autoclass:: Bakery
- :members:
-
-.. autoclass:: Result
- :members:
- :noindex:
-
+++ /dev/null
-:orphan:
-
-.. automodule:: sqlalchemy.ext.declarative
-
-===============
-Declarative API
-===============
-
-API Reference
-=============
-
-.. versionchanged:: 1.4 The fundamental structures of the declarative
- system are now part of SQLAlchemy ORM directly. For these components
- see:
-
- * :func:`_orm.declarative_base`
-
- * :class:`_orm.declared_attr`
-
- * :func:`_orm.has_inherited_table`
-
- * :func:`_orm.synonym_for`
-
- * :meth:`_orm.as_declarative`
-
-See :ref:`declarative_toplevel` for the remaining Declarative extension
-classes.
-
+++ /dev/null
-:orphan:
-
-=========
-Basic Use
-=========
-
-This section has moved to :ref:`orm_declarative_mapping`.
-
-Defining Attributes
-===================
-
-This section is covered by :ref:`mapping_columns_toplevel`
-
-
-
-Accessing the MetaData
-======================
-
-This section has moved to :ref:`orm_declarative_metadata`.
-
-
-Class Constructor
-=================
-
-This section has moved to :ref:`orm_mapper_configuration_overview`.
-
-Mapper Configuration
-====================
-
-This section is moved to :ref:`orm_declarative_mapper_options`.
-
-
-.. _declarative_sql_expressions:
-
-Defining SQL Expressions
-========================
-
-See :ref:`mapper_sql_expressions` for examples on declaratively
-mapping attributes to SQL expressions.
-
+++ /dev/null
-.. _declarative_toplevel:
-
-.. currentmodule:: sqlalchemy.ext.declarative
-
-======================
-Declarative Extensions
-======================
-
-Extensions specific to the :ref:`Declarative <orm_declarative_mapping>`
-mapping API.
-
-.. versionchanged:: 1.4 The vast majority of the Declarative extension is now
- integrated into the SQLAlchemy ORM and is importable from the
- ``sqlalchemy.orm`` namespace. See the documentation at
- :ref:`orm_declarative_mapping` for new documentation.
- For an overview of the change, see :ref:`change_5508`.
-
-.. autoclass:: AbstractConcreteBase
-
-.. autoclass:: ConcreteBase
-
-.. autoclass:: DeferredReflection
- :members:
-
+++ /dev/null
-:orphan:
-
-.. _declarative_inheritance:
-
-Declarative Inheritance
-=======================
-
-See :ref:`inheritance_toplevel` for this section.
+++ /dev/null
-:orphan:
-
-.. _declarative_mixins:
-
-Mixin and Custom Base Classes
-=============================
-
-See :ref:`orm_mixins_toplevel` for this section.
+++ /dev/null
-:orphan:
-
-.. _declarative_configuring_relationships:
-
-=========================
-Configuring Relationships
-=========================
-
-This section is covered by :ref:`orm_declarative_properties`.
-
-.. _declarative_relationship_eval:
-
-Evaluation of relationship arguments
-=====================================
-
-This section is moved to :ref:`orm_declarative_relationship_eval`.
-
-
-.. _declarative_many_to_many:
-
-Configuring Many-to-Many Relationships
-======================================
-
-This section is moved to :ref:`orm_declarative_relationship_secondary_eval`.
-
+++ /dev/null
-:orphan:
-
-.. _declarative_table_args:
-
-===================
-Table Configuration
-===================
-
-This section has moved; see :ref:`orm_declarative_table_configuration`.
-
-
-.. _declarative_hybrid_table:
-
-Using a Hybrid Approach with __table__
-======================================
-
-This section has moved; see :ref:`orm_imperative_table_configuration`.
-
-
-Using Reflection with Declarative
-=================================
-
-This section has moved to :ref:`orm_declarative_reflected`.
-
+++ /dev/null
-.. _horizontal_sharding_toplevel:
-
-Horizontal Sharding
-===================
-
-.. automodule:: sqlalchemy.ext.horizontal_shard
-
-API Documentation
------------------
-
-.. autoclass:: ShardedSession
- :members:
-
-.. autoclass:: set_shard_id
- :members:
-
-.. autoclass:: ShardedQuery
- :members:
-
+++ /dev/null
-.. _hybrids_toplevel:
-
-Hybrid Attributes
-=================
-
-.. automodule:: sqlalchemy.ext.hybrid
-
-API Reference
--------------
-
-.. autoclass:: hybrid_method
- :members:
-
-.. autoclass:: hybrid_property
- :members:
-
-.. autoclass:: Comparator
-
-
-.. autoclass:: HybridExtensionType
- :members:
+++ /dev/null
-.. _plugins:
-.. _sqlalchemy.ext:
-
-ORM Extensions
-==============
-
-SQLAlchemy has a variety of ORM extensions available, which add additional
-functionality to the core behavior.
-
-The extensions build almost entirely on public core and ORM APIs and users should
-be encouraged to read their source code to further their understanding of their
-behavior. In particular the "Horizontal Sharding", "Hybrid Attributes", and
-"Mutation Tracking" extensions are very succinct.
-
-.. toctree::
- :maxdepth: 1
-
- asyncio
- associationproxy
- automap
- baked
- declarative/index
- mypy
- mutable
- orderinglist
- horizontal_shard
- hybrid
- indexable
- instrumentation
-
+++ /dev/null
-.. _indexable_toplevel:
-
-Indexable
-=========
-
-.. automodule:: sqlalchemy.ext.indexable
-
-API Reference
--------------
-
-.. autoclass:: sqlalchemy.ext.indexable.index_property
- :members:
-
+++ /dev/null
-.. _instrumentation_toplevel:
-
-Alternate Class Instrumentation
-===============================
-
-.. automodule:: sqlalchemy.ext.instrumentation
-
-API Reference
--------------
-
-.. autodata:: INSTRUMENTATION_MANAGER
-
-.. autoclass:: sqlalchemy.orm.instrumentation.InstrumentationFactory
-
-.. autoclass:: InstrumentationManager
- :members:
- :undoc-members:
-
-.. autodata:: instrumentation_finders
-
-.. autoclass:: ExtendedInstrumentationRegistry
- :members:
-
-
-
+++ /dev/null
-.. _mutable_toplevel:
-
-Mutation Tracking
-=================
-
-.. automodule:: sqlalchemy.ext.mutable
-
-API Reference
--------------
-
-.. autoclass:: MutableBase
- :members: _parents, coerce
-
-.. autoclass:: Mutable
- :members:
- :inherited-members:
- :private-members:
-
-.. autoclass:: MutableComposite
- :members:
-
-.. autoclass:: MutableDict
- :members:
- :undoc-members:
-
-.. autoclass:: MutableList
- :members:
- :undoc-members:
-
-.. autoclass:: MutableSet
- :members:
- :undoc-members:
-
-
+++ /dev/null
-.. _mypy_toplevel:
-
-Mypy / Pep-484 Support for ORM Mappings
-========================================
-
-Support for :pep:`484` typing annotations as well as the
-MyPy_ type checking tool when using SQLAlchemy
-:ref:`declarative <orm_declarative_mapper_config_toplevel>` mappings
-that refer to the :class:`_schema.Column` object directly, rather than
-the :func:`_orm.mapped_column` construct introduced in SQLAlchemy 2.0.
-
-.. topic:: SQLAlchemy Mypy Plugin Status Update
-
- **Updated December 2022**
-
- For SQLAlchemy 2.0, the Mypy plugin continues to work at the level at which
- it reached in the SQLAlchemy 1.4 release. However, SQLAlchemy 2.0,
- when released, will feature an
- :ref:`all new typing system <whatsnew_20_orm_declarative_typing>`
- for ORM Declarative models that removes the need for the Mypy plugin and
- delivers much more consistent behavior with generally superior capabilities.
- Note that this new capability is **not
- part of SQLAlchemy 1.4, it is only in SQLAlchemy 2.0, which is out with beta
- releases as of December 2022**.
-
- The SQLAlchemy Mypy plugin, while it has technically never left the "alpha"
- stage, should **now be considered as deprecated in SQLAlchemy 2.0, even
- though it is still necessary for full Mypy support when using
- SQLAlchemy 1.4**.
-
- The Mypy plugin itself does not solve the issue of supplying correct typing
- with other typing tools such as Pylance/Pyright, Pytype, Pycharm, etc, which
- cannot make use of Mypy plugins. Additionally, Mypy plugins are extremely
- difficult to develop, maintain and test, as a Mypy plugin must be deeply
- integrated with Mypy's internal datastructures and processes, which itself
- are not stable within the Mypy project itself. The SQLAlchemy Mypy plugin
- has lots of limitations when used with code that deviates from very basic
- patterns which are reported regularly.
-
- For these reasons, new non-regression issues reported against the Mypy
- plugin are unlikely to be fixed. When SQLAlchemy 2.0 is released, it will
- continue to include the plugin, which will have been updated to continue to
- function as well as it does in SQLAlchemy 1.4, when running under SQLAlchemy
- 2.0. **Existing code that passes Mypy checks using the plugin with
- SQLAlchemy 1.4 installed will continue to pass all checks in SQLAlchemy 2.0
- without any changes required, provided the plugin is still used. The
- upcoming API to be released with SQLAlchemy 2.0 is fully backwards
- compatible with the SQLAlchemy 1.4 API and Mypy plugin behavior.**
-
- End-user code that passes all checks under SQLAlchemy 1.4 with the Mypy
- plugin will be able to incrementally migrate to the new structures, once
- that code is running exclusively on SQLAlchemy 2.0. See the section
- :ref:`whatsnew_20_orm_declarative_typing` for background on how this
- migration may proceed.
-
- Code that is running exclusively on SQLAlchemy version
- 2.0 and has fully migrated to the new declarative constructs will enjoy full
- compliance with pep-484 as well as working correctly within IDEs and other
- typing tools, without the need for plugins.
-
-
-Installation
-------------
-
-For **SQLAlchemy 2.0 only**: No stubs should be installed and packages
-like sqlalchemy-stubs_ and sqlalchemy2-stubs_ should be fully uninstalled.
-
-The Mypy_ package itself is a dependency.
-
-Mypy may be installed using the "mypy" extras hook using pip:
-
-.. sourcecode:: text
-
- pip install sqlalchemy[mypy]
-
-The plugin itself is configured as described in
-`Configuring mypy to use Plugins <https://mypy.readthedocs.io/en/latest/extending_mypy.html#configuring-mypy-to-use-plugins>`_,
-using the ``sqlalchemy.ext.mypy.plugin`` module name, such as within
-``setup.cfg``::
-
- [mypy]
- plugins = sqlalchemy.ext.mypy.plugin
-
-.. _sqlalchemy-stubs: https://github.com/dropbox/sqlalchemy-stubs
-
-.. _sqlalchemy2-stubs: https://github.com/sqlalchemy/sqlalchemy2-stubs
-
-What the Plugin Does
---------------------
-
-The primary purpose of the Mypy plugin is to intercept and alter the static
-definition of SQLAlchemy
-:ref:`declarative mappings <orm_declarative_mapper_config_toplevel>` so that
-they match up to how they are structured after they have been
-:term:`instrumented` by their :class:`_orm.Mapper` objects. This allows both
-the class structure itself as well as code that uses the class to make sense to
-the Mypy tool, which otherwise would not be the case based on how declarative
-mappings currently function. The plugin is not unlike similar plugins
-that are required for libraries like
-`dataclasses <https://docs.python.org/3/library/dataclasses.html>`_ which
-alter classes dynamically at runtime.
-
-To cover the major areas where this occurs, consider the following ORM
-mapping, using the typical example of the ``User`` class::
-
- from sqlalchemy import Column, Integer, String, select
- from sqlalchemy.orm import declarative_base
-
- # "Base" is a class that is created dynamically from the
- # declarative_base() function
- Base = declarative_base()
-
-
- class User(Base):
- __tablename__ = "user"
-
- id = Column(Integer, primary_key=True)
- name = Column(String)
-
-
- # "some_user" is an instance of the User class, which
- # accepts "id" and "name" kwargs based on the mapping
- some_user = User(id=5, name="user")
-
- # it has an attribute called .name that's a string
- print(f"Username: {some_user.name}")
-
- # a select() construct makes use of SQL expressions derived from the
- # User class itself
- select_stmt = select(User).where(User.id.in_([3, 4, 5])).where(User.name.contains("s"))
-
-Above, the steps that the Mypy extension can take include:
-
-* Interpretation of the ``Base`` dynamic class generated by
- :func:`_orm.declarative_base`, so that classes which inherit from it
- are known to be mapped. It also can accommodate the class decorator
- approach described at :ref:`orm_declarative_decorator`.
-
-* Type inference for ORM mapped attributes that are defined in declarative
- "inline" style, in the above example the ``id`` and ``name`` attributes of
- the ``User`` class. This includes that an instance of ``User`` will use
- ``int`` for ``id`` and ``str`` for ``name``. It also includes that when the
- ``User.id`` and ``User.name`` class-level attributes are accessed, as they
- are above in the ``select()`` statement, they are compatible with SQL
- expression behavior, which is derived from the
- :class:`_orm.InstrumentedAttribute` attribute descriptor class.
-
-* Application of an ``__init__()`` method to mapped classes that do not
- already include an explicit constructor, which accepts keyword arguments
- of specific types for all mapped attributes detected.
-
-When the Mypy plugin processes the above file, the resulting static class
-definition and Python code passed to the Mypy tool is equivalent to the
-following::
-
- from sqlalchemy import Column, Integer, String, select
- from sqlalchemy.orm import Mapped
- from sqlalchemy.orm.decl_api import DeclarativeMeta
-
-
- class Base(metaclass=DeclarativeMeta):
- __abstract__ = True
-
-
- class User(Base):
- __tablename__ = "user"
-
- id: Mapped[Optional[int]] = Mapped._special_method(
- Column(Integer, primary_key=True)
- )
- name: Mapped[Optional[str]] = Mapped._special_method(Column(String))
-
- def __init__(self, id: Optional[int] = ..., name: Optional[str] = ...) -> None:
- ...
-
-
- some_user = User(id=5, name="user")
-
- print(f"Username: {some_user.name}")
-
- select_stmt = select(User).where(User.id.in_([3, 4, 5])).where(User.name.contains("s"))
-
-The key steps which have been taken above include:
-
-* The ``Base`` class is now defined in terms of the :class:`_orm.DeclarativeMeta`
- class explicitly, rather than being a dynamic class.
-
-* The ``id`` and ``name`` attributes are defined in terms of the
- :class:`_orm.Mapped` class, which represents a Python descriptor that
- exhibits different behaviors at the class vs. instance levels. The
- :class:`_orm.Mapped` class is now the base class for the :class:`_orm.InstrumentedAttribute`
- class that is used for all ORM mapped attributes.
-
- :class:`_orm.Mapped` is defined as a generic class against arbitrary Python
- types, meaning specific occurrences of :class:`_orm.Mapped` are associated
- with a specific Python type, such as ``Mapped[Optional[int]]`` and
- ``Mapped[Optional[str]]`` above.
-
-* The right-hand side of the declarative mapped attribute assignments are
- **removed**, as this resembles the operation that the :class:`_orm.Mapper`
- class would normally be doing, which is that it would be replacing these
- attributes with specific instances of :class:`_orm.InstrumentedAttribute`.
- The original expression is moved into a function call that will allow it to
- still be type-checked without conflicting with the left-hand side of the
- expression. For Mypy purposes, the left-hand typing annotation is sufficient
- for the attribute's behavior to be understood.
-
-* A type stub for the ``User.__init__()`` method is added which includes the
- correct keywords and datatypes.
-
-Usage
-------
-
-The following subsections will address individual uses cases that have
-so far been considered for pep-484 compliance.
-
-
-Introspection of Columns based on TypeEngine
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-For mapped columns that include an explicit datatype, when they are mapped
-as inline attributes, the mapped type will be introspected automatically::
-
- class MyClass(Base):
- # ...
-
- id = Column(Integer, primary_key=True)
- name = Column("employee_name", String(50), nullable=False)
- other_name = Column(String(50))
-
-Above, the ultimate class-level datatypes of ``id``, ``name`` and
-``other_name`` will be introspected as ``Mapped[Optional[int]]``,
-``Mapped[Optional[str]]`` and ``Mapped[Optional[str]]``. The types are by
-default **always** considered to be ``Optional``, even for the primary key and
-non-nullable column. The reason is because while the database columns "id" and
-"name" can't be NULL, the Python attributes ``id`` and ``name`` most certainly
-can be ``None`` without an explicit constructor::
-
- >>> m1 = MyClass()
- >>> m1.id
- None
-
-The types of the above columns can be stated **explicitly**, providing the
-two advantages of clearer self-documentation as well as being able to
-control which types are optional::
-
- class MyClass(Base):
- # ...
-
- id: int = Column(Integer, primary_key=True)
- name: str = Column("employee_name", String(50), nullable=False)
- other_name: Optional[str] = Column(String(50))
-
-The Mypy plugin will accept the above ``int``, ``str`` and ``Optional[str]``
-and convert them to include the ``Mapped[]`` type surrounding them. The
-``Mapped[]`` construct may also be used explicitly::
-
- from sqlalchemy.orm import Mapped
-
-
- class MyClass(Base):
- # ...
-
- id: Mapped[int] = Column(Integer, primary_key=True)
- name: Mapped[str] = Column("employee_name", String(50), nullable=False)
- other_name: Mapped[Optional[str]] = Column(String(50))
-
-When the type is non-optional, it simply means that the attribute as accessed
-from an instance of ``MyClass`` will be considered to be non-None::
-
- mc = MyClass(...)
-
- # will pass mypy --strict
- name: str = mc.name
-
-For optional attributes, Mypy considers that the type must include None
-or otherwise be ``Optional``::
-
- mc = MyClass(...)
-
- # will pass mypy --strict
- other_name: Optional[str] = mc.name
-
-Whether or not the mapped attribute is typed as ``Optional``, the
-generation of the ``__init__()`` method will **still consider all keywords
-to be optional**. This is again matching what the SQLAlchemy ORM actually
-does when it creates the constructor, and should not be confused with the
-behavior of a validating system such as Python ``dataclasses`` which will
-generate a constructor that matches the annotations in terms of optional
-vs. required attributes.
-
-
-Columns that Don't have an Explicit Type
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Columns that include a :class:`_schema.ForeignKey` modifier do not need
-to specify a datatype in a SQLAlchemy declarative mapping. For
-this type of attribute, the Mypy plugin will inform the user that it
-needs an explicit type to be sent::
-
- # .. other imports
- from sqlalchemy.sql.schema import ForeignKey
-
- Base = declarative_base()
-
-
- class User(Base):
- __tablename__ = "user"
-
- id = Column(Integer, primary_key=True)
- name = Column(String)
-
-
- class Address(Base):
- __tablename__ = "address"
-
- id = Column(Integer, primary_key=True)
- user_id = Column(ForeignKey("user.id"))
-
-The plugin will deliver the message as follows:
-
-.. sourcecode:: text
-
- $ mypy test3.py --strict
- test3.py:20: error: [SQLAlchemy Mypy plugin] Can't infer type from
- ORM mapped expression assigned to attribute 'user_id'; please specify a
- Python type or Mapped[<python type>] on the left hand side.
- Found 1 error in 1 file (checked 1 source file)
-
-To resolve, apply an explicit type annotation to the ``Address.user_id``
-column::
-
- class Address(Base):
- __tablename__ = "address"
-
- id = Column(Integer, primary_key=True)
- user_id: int = Column(ForeignKey("user.id"))
-
-Mapping Columns with Imperative Table
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-In :ref:`imperative table style <orm_imperative_table_configuration>`, the
-:class:`_schema.Column` definitions are given inside of a :class:`_schema.Table`
-construct which is separate from the mapped attributes themselves. The Mypy
-plugin does not consider this :class:`_schema.Table`, but instead supports that
-the attributes can be explicitly stated with a complete annotation that
-**must** use the :class:`_orm.Mapped` class to identify them as mapped attributes::
-
- class MyClass(Base):
- __table__ = Table(
- "mytable",
- Base.metadata,
- Column(Integer, primary_key=True),
- Column("employee_name", String(50), nullable=False),
- Column(String(50)),
- )
-
- id: Mapped[int]
- name: Mapped[str]
- other_name: Mapped[Optional[str]]
-
-The above :class:`_orm.Mapped` annotations are considered as mapped columns and
-will be included in the default constructor, as well as provide the correct
-typing profile for ``MyClass`` both at the class level and the instance level.
-
-Mapping Relationships
-^^^^^^^^^^^^^^^^^^^^^^
-
-The plugin has limited support for using type inference to detect the types
-for relationships. For all those cases where it can't detect the type,
-it will emit an informative error message, and in all cases the appropriate
-type may be provided explicitly, either with the :class:`_orm.Mapped`
-class or optionally omitting it for an inline declaration. The plugin
-also needs to determine whether or not the relationship refers to a collection
-or a scalar, and for that it relies upon the explicit value of
-the :paramref:`_orm.relationship.uselist` and/or :paramref:`_orm.relationship.collection_class`
-parameters. An explicit type is needed if neither of these parameters are
-present, as well as if the target type of the :func:`_orm.relationship`
-is a string or callable, and not a class::
-
- class User(Base):
- __tablename__ = "user"
-
- id = Column(Integer, primary_key=True)
- name = Column(String)
-
-
- class Address(Base):
- __tablename__ = "address"
-
- id = Column(Integer, primary_key=True)
- user_id: int = Column(ForeignKey("user.id"))
-
- user = relationship(User)
-
-The above mapping will produce the following error:
-
-.. sourcecode:: text
-
- test3.py:22: error: [SQLAlchemy Mypy plugin] Can't infer scalar or
- collection for ORM mapped expression assigned to attribute 'user'
- if both 'uselist' and 'collection_class' arguments are absent from the
- relationship(); please specify a type annotation on the left hand side.
- Found 1 error in 1 file (checked 1 source file)
-
-The error can be resolved either by using ``relationship(User, uselist=False)``
-or by providing the type, in this case the scalar ``User`` object::
-
- class Address(Base):
- __tablename__ = "address"
-
- id = Column(Integer, primary_key=True)
- user_id: int = Column(ForeignKey("user.id"))
-
- user: User = relationship(User)
-
-For collections, a similar pattern applies, where in the absence of
-``uselist=True`` or a :paramref:`_orm.relationship.collection_class`,
-a collection annotation such as ``List`` may be used. It is also fully
-appropriate to use the string name of the class in the annotation as supported
-by pep-484, ensuring the class is imported with in
-the `TYPE_CHECKING block <https://www.python.org/dev/peps/pep-0484/#runtime-or-type-checking>`_
-as appropriate::
-
- from typing import TYPE_CHECKING, List
-
- from .mymodel import Base
-
- if TYPE_CHECKING:
- # if the target of the relationship is in another module
- # that cannot normally be imported at runtime
- from .myaddressmodel import Address
-
-
- class User(Base):
- __tablename__ = "user"
-
- id = Column(Integer, primary_key=True)
- name = Column(String)
- addresses: List["Address"] = relationship("Address")
-
-As is the case with columns, the :class:`_orm.Mapped` class may also be
-applied explicitly::
-
- class User(Base):
- __tablename__ = "user"
-
- id = Column(Integer, primary_key=True)
- name = Column(String)
-
- addresses: Mapped[List["Address"]] = relationship("Address", back_populates="user")
-
-
- class Address(Base):
- __tablename__ = "address"
-
- id = Column(Integer, primary_key=True)
- user_id: int = Column(ForeignKey("user.id"))
-
- user: Mapped[User] = relationship(User, back_populates="addresses")
-
-.. _mypy_declarative_mixins:
-
-Using @declared_attr and Declarative Mixins
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The :class:`_orm.declared_attr` class allows Declarative mapped attributes to
-be declared in class level functions, and is particularly useful when using
-:ref:`declarative mixins <orm_mixins_toplevel>`. For these functions, the return
-type of the function should be annotated using either the ``Mapped[]``
-construct or by indicating the exact kind of object returned by the function.
-Additionally, "mixin" classes that are not otherwise mapped (i.e. don't extend
-from a :func:`_orm.declarative_base` class nor are they mapped with a method
-such as :meth:`_orm.registry.mapped`) should be decorated with the
-:func:`_orm.declarative_mixin` decorator, which provides a hint to the Mypy
-plugin that a particular class intends to serve as a declarative mixin::
-
- from sqlalchemy.orm import declarative_mixin, declared_attr
-
-
- @declarative_mixin
- class HasUpdatedAt:
- @declared_attr
- def updated_at(cls) -> Column[DateTime]: # uses Column
- return Column(DateTime)
-
-
- @declarative_mixin
- class HasCompany:
- @declared_attr
- def company_id(cls) -> Mapped[int]: # uses Mapped
- return Column(ForeignKey("company.id"))
-
- @declared_attr
- def company(cls) -> Mapped["Company"]:
- return relationship("Company")
-
-
- class Employee(HasUpdatedAt, HasCompany, Base):
- __tablename__ = "employee"
-
- id = Column(Integer, primary_key=True)
- name = Column(String)
-
-Note the mismatch between the actual return type of a method like
-``HasCompany.company`` vs. what is annotated. The Mypy plugin converts
-all ``@declared_attr`` functions into simple annotated attributes to avoid
-this complexity::
-
- # what Mypy sees
- class HasCompany:
- company_id: Mapped[int]
- company: Mapped["Company"]
-
-Combining with Dataclasses or Other Type-Sensitive Attribute Systems
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The examples of Python dataclasses integration at :ref:`orm_declarative_dataclasses`
-presents a problem; Python dataclasses expect an explicit type that it will
-use to build the class, and the value given in each assignment statement
-is significant. That is, a class as follows has to be stated exactly
-as it is in order to be accepted by dataclasses::
-
- mapper_registry: registry = registry()
-
-
- @mapper_registry.mapped
- @dataclass
- class User:
- __table__ = Table(
- "user",
- mapper_registry.metadata,
- Column("id", Integer, primary_key=True),
- Column("name", String(50)),
- Column("fullname", String(50)),
- Column("nickname", String(12)),
- )
- id: int = field(init=False)
- name: Optional[str] = None
- fullname: Optional[str] = None
- nickname: Optional[str] = None
- addresses: List[Address] = field(default_factory=list)
-
- __mapper_args__ = { # type: ignore
- "properties": {"addresses": relationship("Address")}
- }
-
-We can't apply our ``Mapped[]`` types to the attributes ``id``, ``name``,
-etc. because they will be rejected by the ``@dataclass`` decorator. Additionally,
-Mypy has another plugin for dataclasses explicitly which can also get in the
-way of what we're doing.
-
-The above class will actually pass Mypy's type checking without issue; the
-only thing we are missing is the ability for attributes on ``User`` to be
-used in SQL expressions, such as::
-
- stmt = select(User.name).where(User.id.in_([1, 2, 3]))
-
-To provide a workaround for this, the Mypy plugin has an additional feature
-whereby we can specify an extra attribute ``_mypy_mapped_attrs``, that is
-a list that encloses the class-level objects or their string names.
-This attribute can be conditional within the ``TYPE_CHECKING`` variable::
-
- @mapper_registry.mapped
- @dataclass
- class User:
- __table__ = Table(
- "user",
- mapper_registry.metadata,
- Column("id", Integer, primary_key=True),
- Column("name", String(50)),
- Column("fullname", String(50)),
- Column("nickname", String(12)),
- )
- id: int = field(init=False)
- name: Optional[str] = None
- fullname: Optional[str]
- nickname: Optional[str]
- addresses: List[Address] = field(default_factory=list)
-
- if TYPE_CHECKING:
- _mypy_mapped_attrs = [id, name, "fullname", "nickname", addresses]
-
- __mapper_args__ = { # type: ignore
- "properties": {"addresses": relationship("Address")}
- }
-
-With the above recipe, the attributes listed in ``_mypy_mapped_attrs``
-will be applied with the :class:`_orm.Mapped` typing information so that the
-``User`` class will behave as a SQLAlchemy mapped class when used in a
-class-bound context.
-
-.. _Mypy: https://mypy.readthedocs.io/
+++ /dev/null
-Ordering List
-=============
-
-.. automodule:: sqlalchemy.ext.orderinglist
-
-API Reference
--------------
-
-.. autofunction:: ordering_list
-
-.. autofunction:: count_from_0
-
-.. autofunction:: count_from_1
-
-.. autofunction:: count_from_n_factory
-
-.. autoclass:: OrderingList
- :members:
queryguide/index
session
extending
- extensions/index
- examples
+++ /dev/null
-.. highlight:: pycon+sql
-.. doctest-enable
-
-.. currentmodule:: sqlalchemy.orm
-
-.. _largecollections:
-
-Working with Large Collections
-==============================
-
-The default behavior of :func:`_orm.relationship` is to fully load
-the contents of collections into memory, based on a configured
-:ref:`loader strategy <orm_queryguide_relationship_loaders>` that controls
-when and how these contents are loaded from the database. Related collections
-may be loaded into memory not just when they are accessed, or eagerly loaded,
-but in most cases will require population when the collection
-itself is mutated, as well as in cases where the owning object is to be
-deleted by the unit of work system.
-
-When a related collection is potentially very large, it may not be feasible
-for such a collection to be populated into memory under any circumstances,
-as the operation may be overly consuming of time, network and memory
-resources.
-
-This section includes API features intended to allow :func:`_orm.relationship`
-to be used with large collections while maintaining adequate performance.
-
-
-.. _write_only_relationship:
-
-Write Only Relationships
-------------------------
-
-The **write only** loader strategy is the primary means of configuring a
-:func:`_orm.relationship` that will remain writeable, but will not load
-its contents into memory. A write-only ORM configuration in modern
-type-annotated Declarative form is illustrated below::
-
- >>> from decimal import Decimal
- >>> from datetime import datetime
-
- >>> from sqlalchemy import ForeignKey
- >>> from sqlalchemy import func
- >>> from sqlalchemy.orm import DeclarativeBase
- >>> from sqlalchemy.orm import Mapped
- >>> from sqlalchemy.orm import mapped_column
- >>> from sqlalchemy.orm import relationship
- >>> from sqlalchemy.orm import Session
- >>> from sqlalchemy.orm import WriteOnlyMapped
-
- >>> class Base(DeclarativeBase):
- ... pass
-
- >>> class Account(Base):
- ... __tablename__ = "account"
- ... id: Mapped[int] = mapped_column(primary_key=True)
- ... identifier: Mapped[str]
- ...
- ... account_transactions: WriteOnlyMapped["AccountTransaction"] = relationship(
- ... cascade="all, delete-orphan",
- ... passive_deletes=True,
- ... order_by="AccountTransaction.timestamp",
- ... )
- ...
- ... def __repr__(self):
- ... return f"Account(identifier={self.identifier!r})"
-
- >>> class AccountTransaction(Base):
- ... __tablename__ = "account_transaction"
- ... id: Mapped[int] = mapped_column(primary_key=True)
- ... account_id: Mapped[int] = mapped_column(
- ... ForeignKey("account.id", ondelete="cascade")
- ... )
- ... description: Mapped[str]
- ... amount: Mapped[Decimal]
- ... timestamp: Mapped[datetime] = mapped_column(default=func.now())
- ...
- ... def __repr__(self):
- ... return (
- ... f"AccountTransaction(amount={self.amount:.2f}, "
- ... f"timestamp={self.timestamp.isoformat()!r})"
- ... )
- ...
- ... __mapper_args__ = {"eager_defaults": True}
-
-
-.. setup code not for display
-
- >>> from sqlalchemy import create_engine
- >>> from sqlalchemy import event
- >>> engine = create_engine("sqlite://", echo=True)
- >>> @event.listens_for(engine, "connect")
- ... def set_sqlite_pragma(dbapi_connection, connection_record):
- ... cursor = dbapi_connection.cursor()
- ... cursor.execute("PRAGMA foreign_keys=ON")
- ... cursor.close()
-
- >>> Base.metadata.create_all(engine)
- BEGIN...
-
-
-Above, the ``account_transactions`` relationship is configured not using the
-ordinary :class:`.Mapped` annotation, but instead
-using the :class:`.WriteOnlyMapped` type annotation, which at runtime will
-assign the :ref:`loader strategy <orm_queryguide_relationship_loaders>` of
-``lazy="write_only"`` to the target :func:`_orm.relationship`.
-The :class:`.WriteOnlyMapped` annotation is an
-alternative form of the :class:`_orm.Mapped` annotation which indicate the use
-of the :class:`_orm.WriteOnlyCollection` collection type on instances of the
-object.
-
-The above :func:`_orm.relationship` configuration also includes several
-elements that are specific to what action to take when ``Account`` objects
-are deleted, as well as when ``AccountTransaction`` objects are removed from the
-``account_transactions`` collection. These elements are:
-
-* ``passive_deletes=True`` - allows the :term:`unit of work` to forego having
- to load the collection when ``Account`` is deleted; see
- :ref:`passive_deletes`.
-* ``ondelete="cascade"`` configured on the :class:`.ForeignKey` constraint.
- This is also detailed at :ref:`passive_deletes`.
-* ``cascade="all, delete-orphan"`` - instructs the :term:`unit of work` to
- delete ``AccountTransaction`` objects when they are removed from the
- collection. See :ref:`cascade_delete_orphan` in the :ref:`unitofwork_cascades`
- document.
-
-.. versionadded:: 2.0 Added "Write only" relationship loaders.
-
-
-Creating and Persisting New Write Only Collections
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The write-only collection allows for direct assignment of the collection
-as a whole **only** for :term:`transient` or :term:`pending` objects.
-With our above mapping, this indicates we can create a new ``Account``
-object with a sequence of ``AccountTransaction`` objects to be added
-to a :class:`_orm.Session`. Any Python iterable may be used as the
-source of objects to start, where below we use a Python ``list``::
-
- >>> new_account = Account(
- ... identifier="account_01",
- ... account_transactions=[
- ... AccountTransaction(description="initial deposit", amount=Decimal("500.00")),
- ... AccountTransaction(description="transfer", amount=Decimal("1000.00")),
- ... AccountTransaction(description="withdrawal", amount=Decimal("-29.50")),
- ... ],
- ... )
-
- >>> with Session(engine) as session:
- ... session.add(new_account)
- ... session.commit()
- {execsql}BEGIN (implicit)
- INSERT INTO account (identifier) VALUES (?)
- [...] ('account_01',)
- INSERT INTO account_transaction (account_id, description, amount, timestamp)
- VALUES (?, ?, ?, CURRENT_TIMESTAMP) RETURNING id, timestamp
- [... (insertmanyvalues) 1/3 (ordered; batch not supported)] (1, 'initial deposit', 500.0)
- INSERT INTO account_transaction (account_id, description, amount, timestamp)
- VALUES (?, ?, ?, CURRENT_TIMESTAMP) RETURNING id, timestamp
- [insertmanyvalues 2/3 (ordered; batch not supported)] (1, 'transfer', 1000.0)
- INSERT INTO account_transaction (account_id, description, amount, timestamp)
- VALUES (?, ?, ?, CURRENT_TIMESTAMP) RETURNING id, timestamp
- [insertmanyvalues 3/3 (ordered; batch not supported)] (1, 'withdrawal', -29.5)
- COMMIT
-
-
-Once an object is database-persisted (i.e. in the :term:`persistent` or
-:term:`detached` state), the collection has the ability to be extended with new
-items as well as the ability for individual items to be removed. However, the
-collection may **no longer be re-assigned with a full replacement collection**,
-as such an operation requires that the previous collection is fully
-loaded into memory in order to reconcile the old entries with the new ones::
-
- >>> new_account.account_transactions = [
- ... AccountTransaction(description="some transaction", amount=Decimal("10.00"))
- ... ]
- Traceback (most recent call last):
- ...
- sqlalchemy.exc.InvalidRequestError: Collection "Account.account_transactions" does not
- support implicit iteration; collection replacement operations can't be used
-
-Adding New Items to an Existing Collection
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-For write-only collections of persistent objects,
-modifications to the collection using :term:`unit of work` processes may proceed
-only by using the :meth:`.WriteOnlyCollection.add`,
-:meth:`.WriteOnlyCollection.add_all` and :meth:`.WriteOnlyCollection.remove`
-methods::
-
- >>> from sqlalchemy import select
- >>> session = Session(engine, expire_on_commit=False)
- >>> existing_account = session.scalar(select(Account).filter_by(identifier="account_01"))
- {execsql}BEGIN (implicit)
- SELECT account.id, account.identifier
- FROM account
- WHERE account.identifier = ?
- [...] ('account_01',)
- {stop}
- >>> existing_account.account_transactions.add_all(
- ... [
- ... AccountTransaction(description="paycheck", amount=Decimal("2000.00")),
- ... AccountTransaction(description="rent", amount=Decimal("-800.00")),
- ... ]
- ... )
- >>> session.commit()
- {execsql}INSERT INTO account_transaction (account_id, description, amount, timestamp)
- VALUES (?, ?, ?, CURRENT_TIMESTAMP) RETURNING id, timestamp
- [... (insertmanyvalues) 1/2 (ordered; batch not supported)] (1, 'paycheck', 2000.0)
- INSERT INTO account_transaction (account_id, description, amount, timestamp)
- VALUES (?, ?, ?, CURRENT_TIMESTAMP) RETURNING id, timestamp
- [insertmanyvalues 2/2 (ordered; batch not supported)] (1, 'rent', -800.0)
- COMMIT
-
-
-The items added above are held in a pending queue within the
-:class:`_orm.Session` until the next flush, at which point they are INSERTed
-into the database, assuming the added objects were previously :term:`transient`.
-
-Querying Items
-~~~~~~~~~~~~~~
-
-The :class:`_orm.WriteOnlyCollection` does not at any point store a reference
-to the current contents of the collection, nor does it have any behavior where
-it would directly emit a SELECT to the database in order to load them; the
-overriding assumption is that the collection may contain many thousands or
-millions of rows, and should never be fully loaded into memory as a side effect
-of any other operation.
-
-Instead, the :class:`_orm.WriteOnlyCollection` includes SQL-generating helpers
-such as :meth:`_orm.WriteOnlyCollection.select`, which will generate
-a :class:`.Select` construct pre-configured with the correct WHERE / FROM
-criteria for the current parent row, which can then be further modified in
-order to SELECT any range of rows desired, as well as invoked using features
-like :ref:`server side cursors <orm_queryguide_yield_per>` for processes that
-wish to iterate through the full collection in a memory-efficient manner.
-
-The statement generated is illustrated below. Note it also includes ORDER BY
-criteria, indicated in the example mapping by the
-:paramref:`_orm.relationship.order_by` parameter of :func:`_orm.relationship`;
-this criteria would be omitted if the parameter were not configured::
-
- >>> print(existing_account.account_transactions.select())
- {printsql}SELECT account_transaction.id, account_transaction.account_id, account_transaction.description,
- account_transaction.amount, account_transaction.timestamp
- FROM account_transaction
- WHERE :param_1 = account_transaction.account_id ORDER BY account_transaction.timestamp
-
-We may use this :class:`.Select` construct along with the :class:`_orm.Session`
-in order to query for ``AccountTransaction`` objects, most easily using the
-:meth:`_orm.Session.scalars` method that will return a :class:`.Result` that
-yields ORM objects directly. It's typical, though not required, that the
-:class:`.Select` would be modified further to limit the records returned; in
-the example below, additional WHERE criteria to load only "debit" account
-transactions is added, along with "LIMIT 10" to retrieve only the first ten
-rows::
-
- >>> account_transactions = session.scalars(
- ... existing_account.account_transactions.select()
- ... .where(AccountTransaction.amount < 0)
- ... .limit(10)
- ... ).all()
- {execsql}BEGIN (implicit)
- SELECT account_transaction.id, account_transaction.account_id, account_transaction.description,
- account_transaction.amount, account_transaction.timestamp
- FROM account_transaction
- WHERE ? = account_transaction.account_id AND account_transaction.amount < ?
- ORDER BY account_transaction.timestamp LIMIT ? OFFSET ?
- [...] (1, 0, 10, 0)
- {stop}>>> print(account_transactions)
- [AccountTransaction(amount=-29.50, timestamp='...'), AccountTransaction(amount=-800.00, timestamp='...')]
-
-
-Removing Items
-~~~~~~~~~~~~~~
-
-Individual items that are loaded in the :term:`persistent`
-state against the current :class:`_orm.Session` may be marked for removal
-from the collection using the :meth:`.WriteOnlyCollection.remove` method.
-The flush process will implicitly consider the object to be already part
-of the collection when the operation proceeds. The example below
-illustrates removal of an individual ``AccountTransaction`` item,
-which per :ref:`cascade <unitofwork_cascades>` settings results in a
-DELETE of that row::
-
- >>> existing_transaction = account_transactions[0]
- >>> existing_account.account_transactions.remove(existing_transaction)
- >>> session.commit()
- {execsql}DELETE FROM account_transaction WHERE account_transaction.id = ?
- [...] (3,)
- COMMIT
-
-As with any ORM-mapped collection, object removal may proceed either to
-de-associate the object from the collection while leaving the object present in
-the database, or may issue a DELETE for its row, based on the
-:ref:`cascade_delete_orphan` configuration of the :func:`_orm.relationship`.
-
-Collection removal without deletion involves setting foreign key columns to
-NULL for a :ref:`one-to-many <relationship_patterns_o2m>` relationship, or
-deleting the corresponding association row for a
-:ref:`many-to-many <relationships_many_to_many>` relationship.
-
-
-
-Bulk INSERT of New Items
-~~~~~~~~~~~~~~~~~~~~~~~~
-
-The :class:`.WriteOnlyCollection` can generate DML constructs such as
-:class:`_dml.Insert` objects, which may be used in an ORM context to
-produce bulk insert behavior. See the section
-:ref:`orm_queryguide_bulk_insert` for an overview of ORM bulk inserts.
-
-One to Many Collections
-^^^^^^^^^^^^^^^^^^^^^^^
-For a **regular one to many collection only**, the :meth:`.WriteOnlyCollection.insert`
-method will produce an :class:`_dml.Insert` construct which is pre-established with
-VALUES criteria corresponding to the parent object. As this VALUES criteria
-is entirely against the related table, the statement can be used to
-INSERT new rows that will at the same time become new records in the
-related collection::
-
- >>> session.execute(
- ... existing_account.account_transactions.insert(),
- ... [
- ... {"description": "transaction 1", "amount": Decimal("47.50")},
- ... {"description": "transaction 2", "amount": Decimal("-501.25")},
- ... {"description": "transaction 3", "amount": Decimal("1800.00")},
- ... {"description": "transaction 4", "amount": Decimal("-300.00")},
- ... ],
- ... )
- {execsql}BEGIN (implicit)
- INSERT INTO account_transaction (account_id, description, amount, timestamp) VALUES (?, ?, ?, CURRENT_TIMESTAMP)
- [...] [(1, 'transaction 1', 47.5), (1, 'transaction 2', -501.25), (1, 'transaction 3', 1800.0), (1, 'transaction 4', -300.0)]
- <...>
- {stop}
- >>> session.commit()
- COMMIT
-
-.. seealso::
-
- :ref:`orm_queryguide_bulk_insert` - in the :ref:`queryguide_toplevel`
-
- :ref:`relationship_patterns_o2m` - at :ref:`relationship_patterns`
-
-
-Many to Many Collections
-^^^^^^^^^^^^^^^^^^^^^^^^
-
-For a **many to many collection**, the relationship between two classes
-involves a third table that is configured using the
-:paramref:`_orm.relationship.secondary` parameter of :class:`_orm.relationship`.
-To bulk insert rows into a collection of this type using
-:class:`.WriteOnlyCollection`, the new records may be bulk-inserted separately
-first, retrieved using RETURNING, and those records then passed to the
-:meth:`.WriteOnlyCollection.add_all` method where the unit of work process
-will proceed to persist them as part of the collection.
-
-Supposing a class ``BankAudit`` referred to many ``AccountTransaction``
-records using a many-to-many table::
-
- >>> from sqlalchemy import Table, Column
- >>> audit_to_transaction = Table(
- ... "audit_transaction",
- ... Base.metadata,
- ... Column("audit_id", ForeignKey("audit.id", ondelete="CASCADE"), primary_key=True),
- ... Column(
- ... "transaction_id",
- ... ForeignKey("account_transaction.id", ondelete="CASCADE"),
- ... primary_key=True,
- ... ),
- ... )
- >>> class BankAudit(Base):
- ... __tablename__ = "audit"
- ... id: Mapped[int] = mapped_column(primary_key=True)
- ... account_transactions: WriteOnlyMapped["AccountTransaction"] = relationship(
- ... secondary=audit_to_transaction, passive_deletes=True
- ... )
-
-.. setup code not for display
-
- >>> Base.metadata.create_all(engine)
- BEGIN...
-
-To illustrate the two operations, we add more ``AccountTransaction`` objects
-using bulk insert, which we retrieve using RETURNING by adding
-``returning(AccountTransaction)`` to the bulk INSERT statement (note that
-we could just as easily use existing ``AccountTransaction`` objects as well)::
-
- >>> new_transactions = session.scalars(
- ... existing_account.account_transactions.insert().returning(AccountTransaction),
- ... [
- ... {"description": "odd trans 1", "amount": Decimal("50000.00")},
- ... {"description": "odd trans 2", "amount": Decimal("25000.00")},
- ... {"description": "odd trans 3", "amount": Decimal("45.00")},
- ... ],
- ... ).all()
- {execsql}BEGIN (implicit)
- INSERT INTO account_transaction (account_id, description, amount, timestamp) VALUES
- (?, ?, ?, CURRENT_TIMESTAMP), (?, ?, ?, CURRENT_TIMESTAMP), (?, ?, ?, CURRENT_TIMESTAMP)
- RETURNING id, account_id, description, amount, timestamp
- [...] (1, 'odd trans 1', 50000.0, 1, 'odd trans 2', 25000.0, 1, 'odd trans 3', 45.0)
- {stop}
-
-With a list of ``AccountTransaction`` objects ready, the
-:meth:`_orm.WriteOnlyCollection.add_all` method is used to associate many rows
-at once with a new ``BankAudit`` object::
-
- >>> bank_audit = BankAudit()
- >>> session.add(bank_audit)
- >>> bank_audit.account_transactions.add_all(new_transactions)
- >>> session.commit()
- {execsql}INSERT INTO audit DEFAULT VALUES
- [...] ()
- INSERT INTO audit_transaction (audit_id, transaction_id) VALUES (?, ?)
- [...] [(1, 10), (1, 11), (1, 12)]
- COMMIT
-
-.. seealso::
-
- :ref:`orm_queryguide_bulk_insert` - in the :ref:`queryguide_toplevel`
-
- :ref:`relationships_many_to_many` - at :ref:`relationship_patterns`
-
-
-Bulk UPDATE and DELETE of Items
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-In a similar way in which :class:`.WriteOnlyCollection` can generate
-:class:`.Select` constructs with WHERE criteria pre-established, it can
-also generate :class:`.Update` and :class:`.Delete` constructs with that
-same WHERE criteria, to allow criteria-oriented UPDATE and DELETE statements
-against the elements in a large collection.
-
-One To Many Collections
-^^^^^^^^^^^^^^^^^^^^^^^
-
-As is the case with INSERT, this feature is most straightforward with **one
-to many collections**.
-
-In the example below, the :meth:`.WriteOnlyCollection.update` method is used
-to generate an UPDATE statement is emitted against the elements
-in the collection, locating rows where the "amount" is equal to ``-800`` and
-adding the amount of ``200`` to them::
-
- >>> session.execute(
- ... existing_account.account_transactions.update()
- ... .values(amount=AccountTransaction.amount + 200)
- ... .where(AccountTransaction.amount == -800),
- ... )
- {execsql}BEGIN (implicit)
- UPDATE account_transaction SET amount=(account_transaction.amount + ?)
- WHERE ? = account_transaction.account_id AND account_transaction.amount = ?
- [...] (200, 1, -800)
- {stop}<...>
-
-In a similar way, :meth:`.WriteOnlyCollection.delete` will produce a
-DELETE statement that is invoked in the same way::
-
- >>> session.execute(
- ... existing_account.account_transactions.delete().where(
- ... AccountTransaction.amount.between(0, 30)
- ... ),
- ... )
- {execsql}DELETE FROM account_transaction WHERE ? = account_transaction.account_id
- AND account_transaction.amount BETWEEN ? AND ? RETURNING id
- [...] (1, 0, 30)
- <...>
- {stop}
-
-Many to Many Collections
-^^^^^^^^^^^^^^^^^^^^^^^^
-
-.. tip::
-
- The techniques here involve multi-table UPDATE expressions, which are
- slightly more advanced.
-
-For bulk UPDATE and DELETE of **many to many collections**, in order for
-an UPDATE or DELETE statement to relate to the primary key of the
-parent object, the association table must be explicitly part of the
-UPDATE/DELETE statement, which requires
-either that the backend includes supports for non-standard SQL syntaxes,
-or extra explicit steps when constructing the UPDATE or DELETE statement.
-
-For backends that support multi-table versions of UPDATE, the
-:meth:`.WriteOnlyCollection.update` method should work without extra steps
-for a many-to-many collection, as in the example below where an UPDATE
-is emitted against ``AccountTransaction`` objects in terms of the
-many-to-many ``BankAudit.account_transactions`` collection::
-
- >>> session.execute(
- ... bank_audit.account_transactions.update().values(
- ... description=AccountTransaction.description + " (audited)"
- ... )
- ... )
- {execsql}UPDATE account_transaction SET description=(account_transaction.description || ?)
- FROM audit_transaction WHERE ? = audit_transaction.audit_id
- AND account_transaction.id = audit_transaction.transaction_id RETURNING id
- [...] (' (audited)', 1)
- {stop}<...>
-
-The above statement automatically makes use of "UPDATE..FROM" syntax,
-supported by SQLite and others, to name the additional ``audit_transaction``
-table in the WHERE clause.
-
-To UPDATE or DELETE a many-to-many collection where multi-table syntax is
-not available, the many-to-many criteria may be moved into SELECT that
-for example may be combined with IN to match rows.
-The :class:`.WriteOnlyCollection` still helps us here, as we use the
-:meth:`.WriteOnlyCollection.select` method to generate this SELECT for
-us, making use of the :meth:`_sql.Select.with_only_columns` method to
-produce a :term:`scalar subquery`::
-
- >>> from sqlalchemy import update
- >>> subq = bank_audit.account_transactions.select().with_only_columns(AccountTransaction.id)
- >>> session.execute(
- ... update(AccountTransaction)
- ... .values(description=AccountTransaction.description + " (audited)")
- ... .where(AccountTransaction.id.in_(subq))
- ... )
- {execsql}UPDATE account_transaction SET description=(account_transaction.description || ?)
- WHERE account_transaction.id IN (SELECT account_transaction.id
- FROM audit_transaction
- WHERE ? = audit_transaction.audit_id AND account_transaction.id = audit_transaction.transaction_id)
- RETURNING id
- [...] (' (audited)', 1)
- <...>
-
-Write Only Collections - API Documentation
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-
-.. autoclass:: sqlalchemy.orm.WriteOnlyCollection
- :members:
- :inherited-members:
-
-.. autoclass:: sqlalchemy.orm.WriteOnlyMapped
- :members:
-
-.. highlight:: python
-.. doctest-disable
-
-.. _dynamic_relationship:
-
-Dynamic Relationship Loaders
-----------------------------
-
-.. legacy:: The "dynamic" lazy loader strategy is the legacy form of what is
- now the "write_only" strategy described in the section
- :ref:`write_only_relationship`.
-
- The "dynamic" strategy produces a legacy :class:`_orm.Query` object from the
- related collection. However, a major drawback of "dynamic" relationships is
- that there are several cases where the collection will fully iterate, some
- of which are non-obvious, which can only be prevented with careful
- programming and testing on a case-by-case basis. Therefore, for truly large
- collection management, the :class:`_orm.WriteOnlyCollection` should be
- preferred.
-
- The dynamic loader is also not compatible with the :ref:`asyncio_toplevel`
- extension. It can be used with some limitations, as indicated in
- :ref:`Asyncio dynamic guidelines <dynamic_asyncio>`, but again the
- :class:`_orm.WriteOnlyCollection`, which is fully compatible with asyncio,
- should be preferred.
-
-The dynamic relationship strategy allows configuration of a
-:func:`_orm.relationship` which when accessed on an instance will return a
-legacy :class:`_orm.Query` object in place of the collection. The
-:class:`_orm.Query` can then be modified further so that the database
-collection may be iterated based on filtering criteria. The returned
-:class:`_orm.Query` object is an instance of :class:`_orm.AppenderQuery`, which
-combines the loading and iteration behavior of :class:`_orm.Query` along with
-rudimentary collection mutation methods such as
-:meth:`_orm.AppenderQuery.append` and :meth:`_orm.AppenderQuery.remove`.
-
-The "dynamic" loader strategy may be configured with
-type-annotated Declarative form using the :class:`_orm.DynamicMapped`
-annotation class::
-
- from sqlalchemy.orm import DynamicMapped
-
-
- class User(Base):
- __tablename__ = "user"
-
- id: Mapped[int] = mapped_column(primary_key=True)
- posts: DynamicMapped[Post] = relationship()
-
-Above, the ``User.posts`` collection on an individual ``User`` object
-will return the :class:`_orm.AppenderQuery` object, which is a subclass
-of :class:`_orm.Query` that also supports basic collection mutation
-operations::
-
-
- jack = session.get(User, id)
-
- # filter Jack's blog posts
- posts = jack.posts.filter(Post.headline == "this is a post")
-
- # apply array slices
- posts = jack.posts[5:20]
-
-The dynamic relationship supports limited write operations, via the
-:meth:`_orm.AppenderQuery.append` and :meth:`_orm.AppenderQuery.remove` methods::
-
- oldpost = jack.posts.filter(Post.headline == "old post").one()
- jack.posts.remove(oldpost)
-
- jack.posts.append(Post("new post"))
-
-Since the read side of the dynamic relationship always queries the
-database, changes to the underlying collection will not be visible
-until the data has been flushed. However, as long as "autoflush" is
-enabled on the :class:`.Session` in use, this will occur
-automatically each time the collection is about to emit a
-query.
-
-
-Dynamic Relationship Loaders - API
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. autoclass:: sqlalchemy.orm.AppenderQuery
- :members:
- :inherited-members: Query
-
-.. autoclass:: sqlalchemy.orm.DynamicMapped
- :members:
-
-.. _collections_raiseload:
-
-Setting RaiseLoad
------------------
-
-A "raise"-loaded relationship will raise an
-:exc:`~sqlalchemy.exc.InvalidRequestError` where the attribute would normally
-emit a lazy load::
-
- class MyClass(Base):
- __tablename__ = "some_table"
-
- # ...
-
- children: Mapped[List[MyRelatedClass]] = relationship(lazy="raise")
-
-Above, attribute access on the ``children`` collection will raise an exception
-if it was not previously populated. This includes read access but for
-collections will also affect write access, as collections can't be mutated
-without first loading them. The rationale for this is to ensure that an
-application is not emitting any unexpected lazy loads within a certain context.
-Rather than having to read through SQL logs to determine that all necessary
-attributes were eager loaded, the "raise" strategy will cause unloaded
-attributes to raise immediately if accessed. The raise strategy is
-also available on a query option basis using the :func:`_orm.raiseload`
-loader option.
-
-.. seealso::
-
- :ref:`prevent_lazy_with_raiseload`
-
-Using Passive Deletes
----------------------
-
-An important aspect of collection management in SQLAlchemy is that when an
-object that refers to a collection is deleted, SQLAlchemy needs to consider the
-objects that are inside this collection. Those objects will need to be
-de-associated from the parent, which for a one-to-many collection would mean
-that foreign key columns are set to NULL, or based on
-:ref:`cascade <unitofwork_cascades>` settings, may instead want to emit a
-DELETE for these rows.
-
-The :term:`unit of work` process only considers objects on a row-by-row basis,
-meaning a DELETE operation implies that all rows within a collection must be
-fully loaded into memory inside the flush process. This is not feasible for
-large collections, so we instead seek to rely upon the database's own
-capability to update or delete the rows automatically using foreign key ON
-DELETE rules, instructing the unit of work to forego actually needing to load
-these rows in order to handle them. The unit of work can be instructed to work
-in this manner by configuring :paramref:`_orm.relationship.passive_deletes` on
-the :func:`_orm.relationship` construct; the foreign key constraints in use
-must also be correctly configured.
-
-For further detail on a complete "passive delete" configuration, see the
-section :ref:`passive_deletes`.
-
-
-
+++ /dev/null
-:orphan:
-
-This document has moved to :doc:`queryguide/columns`
-
+++ /dev/null
-.. currentmodule:: sqlalchemy.orm
-
-.. _mapper_sql_expressions:
-
-SQL Expressions as Mapped Attributes
-====================================
-
-Attributes on a mapped class can be linked to SQL expressions, which can
-be used in queries.
-
-Using a Hybrid
---------------
-
-The easiest and most flexible way to link relatively simple SQL expressions to a class is to use a so-called
-"hybrid attribute",
-described in the section :ref:`hybrids_toplevel`. The hybrid provides
-for an expression that works at both the Python level as well as at the
-SQL expression level. For example, below we map a class ``User``,
-containing attributes ``firstname`` and ``lastname``, and include a hybrid that
-will provide for us the ``fullname``, which is the string concatenation of the two::
-
- from sqlalchemy.ext.hybrid import hybrid_property
-
-
- class User(Base):
- __tablename__ = "user"
- id = mapped_column(Integer, primary_key=True)
- firstname = mapped_column(String(50))
- lastname = mapped_column(String(50))
-
- @hybrid_property
- def fullname(self):
- return self.firstname + " " + self.lastname
-
-Above, the ``fullname`` attribute is interpreted at both the instance and
-class level, so that it is available from an instance::
-
- some_user = session.scalars(select(User).limit(1)).first()
- print(some_user.fullname)
-
-as well as usable within queries::
-
- some_user = session.scalars(
- select(User).where(User.fullname == "John Smith").limit(1)
- ).first()
-
-The string concatenation example is a simple one, where the Python expression
-can be dual purposed at the instance and class level. Often, the SQL expression
-must be distinguished from the Python expression, which can be achieved using
-:meth:`.hybrid_property.expression`. Below we illustrate the case where a conditional
-needs to be present inside the hybrid, using the ``if`` statement in Python and the
-:func:`_expression.case` construct for SQL expressions::
-
- from sqlalchemy.ext.hybrid import hybrid_property
- from sqlalchemy.sql import case
-
-
- class User(Base):
- __tablename__ = "user"
- id = mapped_column(Integer, primary_key=True)
- firstname = mapped_column(String(50))
- lastname = mapped_column(String(50))
-
- @hybrid_property
- def fullname(self):
- if self.firstname is not None:
- return self.firstname + " " + self.lastname
- else:
- return self.lastname
-
- @fullname.expression
- def fullname(cls):
- return case(
- (cls.firstname != None, cls.firstname + " " + cls.lastname),
- else_=cls.lastname,
- )
-
-.. _mapper_column_property_sql_expressions:
-
-Using column_property
----------------------
-
-The :func:`_orm.column_property` function can be used to map a SQL
-expression in a manner similar to a regularly mapped :class:`_schema.Column`.
-With this technique, the attribute is loaded
-along with all other column-mapped attributes at load time. This is in some
-cases an advantage over the usage of hybrids, as the value can be loaded
-up front at the same time as the parent row of the object, particularly if
-the expression is one which links to other tables (typically as a correlated
-subquery) to access data that wouldn't normally be
-available on an already loaded object.
-
-Disadvantages to using :func:`_orm.column_property` for SQL expressions include that
-the expression must be compatible with the SELECT statement emitted for the class
-as a whole, and there are also some configurational quirks which can occur
-when using :func:`_orm.column_property` from declarative mixins.
-
-Our "fullname" example can be expressed using :func:`_orm.column_property` as
-follows::
-
- from sqlalchemy.orm import column_property
-
-
- class User(Base):
- __tablename__ = "user"
- id = mapped_column(Integer, primary_key=True)
- firstname = mapped_column(String(50))
- lastname = mapped_column(String(50))
- fullname = column_property(firstname + " " + lastname)
-
-Correlated subqueries may be used as well. Below we use the
-:func:`_expression.select` construct to create a :class:`_sql.ScalarSelect`,
-representing a column-oriented SELECT statement, that links together the count
-of ``Address`` objects available for a particular ``User``::
-
- from sqlalchemy.orm import column_property
- from sqlalchemy import select, func
- from sqlalchemy import Column, Integer, String, ForeignKey
-
- from sqlalchemy.orm import DeclarativeBase
-
-
- class Base(DeclarativeBase):
- pass
-
-
- class Address(Base):
- __tablename__ = "address"
- id = mapped_column(Integer, primary_key=True)
- user_id = mapped_column(Integer, ForeignKey("user.id"))
-
-
- class User(Base):
- __tablename__ = "user"
- id = mapped_column(Integer, primary_key=True)
- address_count = column_property(
- select(func.count(Address.id))
- .where(Address.user_id == id)
- .correlate_except(Address)
- .scalar_subquery()
- )
-
-In the above example, we define a :func:`_expression.ScalarSelect` construct like the following::
-
- stmt = (
- select(func.count(Address.id))
- .where(Address.user_id == id)
- .correlate_except(Address)
- .scalar_subquery()
- )
-
-Above, we first use :func:`_sql.select` to create a :class:`_sql.Select`
-construct, which we then convert into a :term:`scalar subquery` using the
-:meth:`_sql.Select.scalar_subquery` method, indicating our intent to use this
-:class:`_sql.Select` statement in a column expression context.
-
-Within the :class:`_sql.Select` itself, we select the count of ``Address.id`` rows
-where the ``Address.user_id`` column is equated to ``id``, which in the context
-of the ``User`` class is the :class:`_schema.Column` named ``id`` (note that ``id`` is
-also the name of a Python built in function, which is not what we want to use
-here - if we were outside of the ``User`` class definition, we'd use ``User.id``).
-
-The :meth:`_sql.Select.correlate_except` method indicates that each element in the
-FROM clause of this :func:`_expression.select` may be omitted from the FROM list (that is, correlated
-to the enclosing SELECT statement against ``User``) except for the one corresponding
-to ``Address``. This isn't strictly necessary, but prevents ``Address`` from
-being inadvertently omitted from the FROM list in the case of a long string
-of joins between ``User`` and ``Address`` tables where SELECT statements against
-``Address`` are nested.
-
-For a :func:`.column_property` that refers to columns linked from a
-many-to-many relationship, use :func:`.and_` to join the fields of the
-association table to both tables in a relationship::
-
- from sqlalchemy import and_
-
-
- class Author(Base):
- # ...
-
- book_count = column_property(
- select(func.count(books.c.id))
- .where(
- and_(
- book_authors.c.author_id == authors.c.id,
- book_authors.c.book_id == books.c.id,
- )
- )
- .scalar_subquery()
- )
-
-Adding column_property() to an existing Declarative mapped class
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-If import issues prevent the :func:`.column_property` from being defined
-inline with the class, it can be assigned to the class after both
-are configured. When using mappings that make use of a Declarative
-base class (i.e. produced by the :class:`_orm.DeclarativeBase` superclass
-or legacy functions such as :func:`_orm.declarative_base`),
-this attribute assignment has the effect of calling :meth:`_orm.Mapper.add_property`
-to add an additional property after the fact::
-
- # only works if a declarative base class is in use
- User.address_count = column_property(
- select(func.count(Address.id)).where(Address.user_id == User.id).scalar_subquery()
- )
-
-When using mapping styles that don't use Declarative base classes
-such as the :meth:`_orm.registry.mapped` decorator, the :meth:`_orm.Mapper.add_property`
-method may be invoked explicitly on the underlying :class:`_orm.Mapper` object,
-which can be obtained using :func:`_sa.inspect`::
-
- from sqlalchemy.orm import registry
-
- reg = registry()
-
-
- @reg.mapped
- class User:
- __tablename__ = "user"
-
- # ... additional mapping directives
-
-
- # later ...
-
- # works for any kind of mapping
- from sqlalchemy import inspect
-
- inspect(User).add_property(
- column_property(
- select(func.count(Address.id))
- .where(Address.user_id == User.id)
- .scalar_subquery()
- )
- )
-
-.. seealso::
-
- :ref:`orm_declarative_table_adding_columns`
-
-
-.. _mapper_column_property_sql_expressions_composed:
-
-Composing from Column Properties at Mapping Time
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-It is possible to create mappings that combine multiple
-:class:`.ColumnProperty` objects together. The :class:`.ColumnProperty` will
-be interpreted as a SQL expression when used in a Core expression context,
-provided that it is targeted by an existing expression object; this works by
-the Core detecting that the object has a ``__clause_element__()`` method which
-returns a SQL expression. However, if the :class:`.ColumnProperty` is used as
-a lead object in an expression where there is no other Core SQL expression
-object to target it, the :attr:`.ColumnProperty.expression` attribute will
-return the underlying SQL expression so that it can be used to build SQL
-expressions consistently. Below, the ``File`` class contains an attribute
-``File.path`` that concatenates a string token to the ``File.filename``
-attribute, which is itself a :class:`.ColumnProperty`::
-
-
- class File(Base):
- __tablename__ = "file"
-
- id = mapped_column(Integer, primary_key=True)
- name = mapped_column(String(64))
- extension = mapped_column(String(8))
- filename = column_property(name + "." + extension)
- path = column_property("C:/" + filename.expression)
-
-When the ``File`` class is used in expressions normally, the attributes
-assigned to ``filename`` and ``path`` are usable directly. The use of the
-:attr:`.ColumnProperty.expression` attribute is only necessary when using
-the :class:`.ColumnProperty` directly within the mapping definition::
-
- stmt = select(File.path).where(File.filename == "foo.txt")
-
-Using Column Deferral with ``column_property()``
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The column deferral feature introduced in the :ref:`queryguide_toplevel`
-at :ref:`orm_queryguide_column_deferral` may be applied at mapping time
-to a SQL expression mapped by :func:`_orm.column_property` by using the
-:func:`_orm.deferred` function in place of :func:`_orm.column_property`::
-
- from sqlalchemy.orm import deferred
-
-
- class User(Base):
- __tablename__ = "user"
-
- id: Mapped[int] = mapped_column(primary_key=True)
- firstname: Mapped[str] = mapped_column()
- lastname: Mapped[str] = mapped_column()
- fullname: Mapped[str] = deferred(firstname + " " + lastname)
-
-.. seealso::
-
- :ref:`orm_queryguide_deferred_imperative`
-
-
-
-Using a plain descriptor
-------------------------
-
-In cases where a SQL query more elaborate than what :func:`_orm.column_property`
-or :class:`.hybrid_property` can provide must be emitted, a regular Python
-function accessed as an attribute can be used, assuming the expression
-only needs to be available on an already-loaded instance. The function
-is decorated with Python's own ``@property`` decorator to mark it as a read-only
-attribute. Within the function, :func:`.object_session`
-is used to locate the :class:`.Session` corresponding to the current object,
-which is then used to emit a query::
-
- from sqlalchemy.orm import object_session
- from sqlalchemy import select, func
-
-
- class User(Base):
- __tablename__ = "user"
- id = mapped_column(Integer, primary_key=True)
- firstname = mapped_column(String(50))
- lastname = mapped_column(String(50))
-
- @property
- def address_count(self):
- return object_session(self).scalar(
- select(func.count(Address.id)).where(Address.user_id == self.id)
- )
-
-The plain descriptor approach is useful as a last resort, but is less performant
-in the usual case than both the hybrid and column property approaches, in that
-it needs to emit a SQL query upon each access.
-
-.. _mapper_querytime_expression:
-
-Query-time SQL expressions as mapped attributes
------------------------------------------------
-
-In addition to being able to configure fixed SQL expressions on mapped classes,
-the SQLAlchemy ORM also includes a feature wherein objects may be loaded
-with the results of arbitrary SQL expressions which are set up at query time as part
-of their state. This behavior is available by configuring an ORM mapped
-attribute using :func:`_orm.query_expression` and then using the
-:func:`_orm.with_expression` loader option at query time. See the section
-:ref:`orm_queryguide_with_expression` for an example mapping and usage.
-
mapping_styles
declarative_mapping
- dataclasses
- mapped_sql_expr
mapped_attributes
- composites
inheritance
nonstandard_mappings
- versioning
mapping_api
-.. toctree::
- :hidden:
-
- scalar_mapping
+++ /dev/null
-.. _mapping_columns_toplevel:
-
-Mapping Table Columns
-=====================
-
-This section has been integrated into the
-:ref:`orm_declarative_table_config_toplevel` Declarative section.
-
-
basic_relationships
self_referential
join_conditions
- large_collections
collection_api
relationship_persistence
- backref
relationship_api
+++ /dev/null
-===============================
-Mapping SQL Expressions
-===============================
-
-This page has been merged into the
-:ref:`mapper_config_toplevel` index.
-
-
-.. toctree::
- :hidden:
-
- mapping_columns
-
:maxdepth: 3
session_basics
- session_state_management
cascades
session_transaction
persistence_techniques
- contextual
session_events
session_api
+++ /dev/null
-State Management
-================
-
-.. _session_object_states:
-
-Quickie Intro to Object States
-------------------------------
-
-It's helpful to know the states which an instance can have within a session:
-
-* **Transient** - an instance that's not in a session, and is not saved to the
- database; i.e. it has no database identity. The only relationship such an
- object has to the ORM is that its class has a :class:`_orm.Mapper` associated
- with it.
-
-* **Pending** - when you :meth:`~.Session.add` a transient
- instance, it becomes pending. It still wasn't actually flushed to the
- database yet, but it will be when the next flush occurs.
-
-* **Persistent** - An instance which is present in the session and has a record
- in the database. You get persistent instances by either flushing so that the
- pending instances become persistent, or by querying the database for
- existing instances (or moving persistent instances from other sessions into
- your local session).
-
-* **Deleted** - An instance which has been deleted within a flush, but
- the transaction has not yet completed. Objects in this state are essentially
- in the opposite of "pending" state; when the session's transaction is committed,
- the object will move to the detached state. Alternatively, when
- the session's transaction is rolled back, a deleted object moves
- *back* to the persistent state.
-
-* **Detached** - an instance which corresponds, or previously corresponded,
- to a record in the database, but is not currently in any session.
- The detached object will contain a database identity marker, however
- because it is not associated with a session, it is unknown whether or not
- this database identity actually exists in a target database. Detached
- objects are safe to use normally, except that they have no ability to
- load unloaded attributes or attributes that were previously marked
- as "expired".
-
-For a deeper dive into all possible state transitions, see the
-section :ref:`session_lifecycle_events` which describes each transition
-as well as how to programmatically track each one.
-
-Getting the Current State of an Object
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The actual state of any mapped object can be viewed at any time using
-the :func:`_sa.inspect` function on a mapped instance; this function will
-return the corresponding :class:`.InstanceState` object which manages the
-internal ORM state for the object. :class:`.InstanceState` provides, among
-other accessors, boolean attributes indicating the persistence state
-of the object, including:
-
-* :attr:`.InstanceState.transient`
-* :attr:`.InstanceState.pending`
-* :attr:`.InstanceState.persistent`
-* :attr:`.InstanceState.deleted`
-* :attr:`.InstanceState.detached`
-
-E.g.::
-
- >>> from sqlalchemy import inspect
- >>> insp = inspect(my_object)
- >>> insp.persistent
- True
-
-.. seealso::
-
- :ref:`orm_mapper_inspection_instancestate` - further examples of
- :class:`.InstanceState`
-
-.. _session_attributes:
-
-Session Attributes
-------------------
-
-The :class:`~sqlalchemy.orm.session.Session` itself acts somewhat like a
-set-like collection. All items present may be accessed using the iterator
-interface::
-
- for obj in session:
- print(obj)
-
-And presence may be tested for using regular "contains" semantics::
-
- if obj in session:
- print("Object is present")
-
-The session is also keeping track of all newly created (i.e. pending) objects,
-all objects which have had changes since they were last loaded or saved (i.e.
-"dirty"), and everything that's been marked as deleted::
-
- # pending objects recently added to the Session
- session.new
-
- # persistent objects which currently have changes detected
- # (this collection is now created on the fly each time the property is called)
- session.dirty
-
- # persistent objects that have been marked as deleted via session.delete(obj)
- session.deleted
-
- # dictionary of all persistent objects, keyed on their
- # identity key
- session.identity_map
-
-(Documentation: :attr:`.Session.new`, :attr:`.Session.dirty`,
-:attr:`.Session.deleted`, :attr:`.Session.identity_map`).
-
-
-.. _session_referencing_behavior:
-
-Session Referencing Behavior
-----------------------------
-
-Objects within the session are *weakly referenced*. This
-means that when they are dereferenced in the outside application, they fall
-out of scope from within the :class:`~sqlalchemy.orm.session.Session` as well
-and are subject to garbage collection by the Python interpreter. The
-exceptions to this include objects which are pending, objects which are marked
-as deleted, or persistent objects which have pending changes on them. After a
-full flush, these collections are all empty, and all objects are again weakly
-referenced.
-
-To cause objects in the :class:`.Session` to remain strongly
-referenced, usually a simple approach is all that's needed. Examples
-of externally managed strong-referencing behavior include loading
-objects into a local dictionary keyed to their primary key, or into
-lists or sets for the span of time that they need to remain
-referenced. These collections can be associated with a
-:class:`.Session`, if desired, by placing them into the
-:attr:`.Session.info` dictionary.
-
-An event based approach is also feasible. A simple recipe that provides
-"strong referencing" behavior for all objects as they remain within
-the :term:`persistent` state is as follows::
-
- from sqlalchemy import event
-
-
- def strong_reference_session(session):
- @event.listens_for(session, "pending_to_persistent")
- @event.listens_for(session, "deleted_to_persistent")
- @event.listens_for(session, "detached_to_persistent")
- @event.listens_for(session, "loaded_as_persistent")
- def strong_ref_object(sess, instance):
- if "refs" not in sess.info:
- sess.info["refs"] = refs = set()
- else:
- refs = sess.info["refs"]
-
- refs.add(instance)
-
- @event.listens_for(session, "persistent_to_detached")
- @event.listens_for(session, "persistent_to_deleted")
- @event.listens_for(session, "persistent_to_transient")
- def deref_object(sess, instance):
- sess.info["refs"].discard(instance)
-
-Above, we intercept the :meth:`.SessionEvents.pending_to_persistent`,
-:meth:`.SessionEvents.detached_to_persistent`,
-:meth:`.SessionEvents.deleted_to_persistent` and
-:meth:`.SessionEvents.loaded_as_persistent` event hooks in order to intercept
-objects as they enter the :term:`persistent` transition, and the
-:meth:`.SessionEvents.persistent_to_detached` and
-:meth:`.SessionEvents.persistent_to_deleted` hooks to intercept
-objects as they leave the persistent state.
-
-The above function may be called for any :class:`.Session` in order to
-provide strong-referencing behavior on a per-:class:`.Session` basis::
-
- from sqlalchemy.orm import Session
-
- my_session = Session()
- strong_reference_session(my_session)
-
-It may also be called for any :class:`.sessionmaker`::
-
- from sqlalchemy.orm import sessionmaker
-
- maker = sessionmaker()
- strong_reference_session(maker)
-
-.. _unitofwork_merging:
-
-Merging
--------
-
-:meth:`~.Session.merge` transfers state from an
-outside object into a new or already existing instance within a session. It
-also reconciles the incoming data against the state of the
-database, producing a history stream which will be applied towards the next
-flush, or alternatively can be made to produce a simple "transfer" of
-state without producing change history or accessing the database. Usage is as follows::
-
- merged_object = session.merge(existing_object)
-
-When given an instance, it follows these steps:
-
-* It examines the primary key of the instance. If it's present, it attempts
- to locate that instance in the local identity map. If the ``load=True``
- flag is left at its default, it also checks the database for this primary
- key if not located locally.
-* If the given instance has no primary key, or if no instance can be found
- with the primary key given, a new instance is created.
-* The state of the given instance is then copied onto the located/newly created
- instance. For attribute values which are present on the source instance, the
- value is transferred to the target instance. For attribute values that aren't
- present on the source instance, the corresponding attribute on the target
- instance is :term:`expired` from memory, which discards any locally
- present value from the target instance for that attribute, but no
- direct modification is made to the database-persisted value for that
- attribute.
-
- If the ``load=True`` flag is left at its default,
- this copy process emits events and will load the target object's
- unloaded collections for each attribute present on the source object,
- so that the incoming state can be reconciled against what's
- present in the database. If ``load``
- is passed as ``False``, the incoming data is "stamped" directly without
- producing any history.
-* The operation is cascaded to related objects and collections, as
- indicated by the ``merge`` cascade (see :ref:`unitofwork_cascades`).
-* The new instance is returned.
-
-With :meth:`~.Session.merge`, the given "source"
-instance is not modified nor is it associated with the target :class:`.Session`,
-and remains available to be merged with any number of other :class:`.Session`
-objects. :meth:`~.Session.merge` is useful for
-taking the state of any kind of object structure without regard for its
-origins or current session associations and copying its state into a
-new session. Here's some examples:
-
-* An application which reads an object structure from a file and wishes to
- save it to the database might parse the file, build up the
- structure, and then use
- :meth:`~.Session.merge` to save it
- to the database, ensuring that the data within the file is
- used to formulate the primary key of each element of the
- structure. Later, when the file has changed, the same
- process can be re-run, producing a slightly different
- object structure, which can then be ``merged`` in again,
- and the :class:`~sqlalchemy.orm.session.Session` will
- automatically update the database to reflect those
- changes, loading each object from the database by primary key and
- then updating its state with the new state given.
-
-* An application is storing objects in an in-memory cache, shared by
- many :class:`.Session` objects simultaneously. :meth:`~.Session.merge`
- is used each time an object is retrieved from the cache to create
- a local copy of it in each :class:`.Session` which requests it.
- The cached object remains detached; only its state is moved into
- copies of itself that are local to individual :class:`~.Session`
- objects.
-
- In the caching use case, it's common to use the ``load=False``
- flag to remove the overhead of reconciling the object's state
- with the database. There's also a "bulk" version of
- :meth:`~.Session.merge` called :meth:`_query.Query.merge_result`
- that was designed to work with cache-extended :class:`_query.Query`
- objects - see the section :ref:`examples_caching`.
-
-* An application wants to transfer the state of a series of objects
- into a :class:`.Session` maintained by a worker thread or other
- concurrent system. :meth:`~.Session.merge` makes a copy of each object
- to be placed into this new :class:`.Session`. At the end of the operation,
- the parent thread/process maintains the objects it started with,
- and the thread/worker can proceed with local copies of those objects.
-
- In the "transfer between threads/processes" use case, the application
- may want to use the ``load=False`` flag as well to avoid overhead and
- redundant SQL queries as the data is transferred.
-
-Merge Tips
-~~~~~~~~~~
-
-:meth:`~.Session.merge` is an extremely useful method for many purposes. However,
-it deals with the intricate border between objects that are transient/detached and
-those that are persistent, as well as the automated transference of state.
-The wide variety of scenarios that can present themselves here often require a
-more careful approach to the state of objects. Common problems with merge usually involve
-some unexpected state regarding the object being passed to :meth:`~.Session.merge`.
-
-Lets use the canonical example of the User and Address objects::
-
- class User(Base):
- __tablename__ = "user"
-
- id = mapped_column(Integer, primary_key=True)
- name = mapped_column(String(50), nullable=False)
- addresses = relationship("Address", backref="user")
-
-
- class Address(Base):
- __tablename__ = "address"
-
- id = mapped_column(Integer, primary_key=True)
- email_address = mapped_column(String(50), nullable=False)
- user_id = mapped_column(Integer, ForeignKey("user.id"), nullable=False)
-
-Assume a ``User`` object with one ``Address``, already persistent::
-
- >>> u1 = User(name="ed", addresses=[Address(email_address="ed@ed.com")])
- >>> session.add(u1)
- >>> session.commit()
-
-We now create ``a1``, an object outside the session, which we'd like
-to merge on top of the existing ``Address``::
-
- >>> existing_a1 = u1.addresses[0]
- >>> a1 = Address(id=existing_a1.id)
-
-A surprise would occur if we said this::
-
- >>> a1.user = u1
- >>> a1 = session.merge(a1)
- >>> session.commit()
- sqlalchemy.orm.exc.FlushError: New instance <Address at 0x1298f50>
- with identity key (<class '__main__.Address'>, (1,)) conflicts with
- persistent instance <Address at 0x12a25d0>
-
-Why is that ? We weren't careful with our cascades. The assignment
-of ``a1.user`` to a persistent object cascaded to the backref of ``User.addresses``
-and made our ``a1`` object pending, as though we had added it. Now we have
-*two* ``Address`` objects in the session::
-
- >>> a1 = Address()
- >>> a1.user = u1
- >>> a1 in session
- True
- >>> existing_a1 in session
- True
- >>> a1 is existing_a1
- False
-
-Above, our ``a1`` is already pending in the session. The
-subsequent :meth:`~.Session.merge` operation essentially
-does nothing. Cascade can be configured via the :paramref:`_orm.relationship.cascade`
-option on :func:`_orm.relationship`, although in this case it
-would mean removing the ``save-update`` cascade from the
-``User.addresses`` relationship - and usually, that behavior
-is extremely convenient. The solution here would usually be to not assign
-``a1.user`` to an object already persistent in the target
-session.
-
-The ``cascade_backrefs=False`` option of :func:`_orm.relationship`
-will also prevent the ``Address`` from
-being added to the session via the ``a1.user = u1`` assignment.
-
-Further detail on cascade operation is at :ref:`unitofwork_cascades`.
-
-Another example of unexpected state::
-
- >>> a1 = Address(id=existing_a1.id, user_id=u1.id)
- >>> a1.user = None
- >>> a1 = session.merge(a1)
- >>> session.commit()
- sqlalchemy.exc.IntegrityError: (IntegrityError) address.user_id
- may not be NULL
-
-Above, the assignment of ``user`` takes precedence over the foreign key
-assignment of ``user_id``, with the end result that ``None`` is applied
-to ``user_id``, causing a failure.
-
-Most :meth:`~.Session.merge` issues can be examined by first checking -
-is the object prematurely in the session ?
-
-.. sourcecode:: pycon+sql
-
- >>> a1 = Address(id=existing_a1, user_id=user.id)
- >>> assert a1 not in session
- >>> a1 = session.merge(a1)
-
-Or is there state on the object that we don't want ? Examining ``__dict__``
-is a quick way to check::
-
- >>> a1 = Address(id=existing_a1, user_id=user.id)
- >>> a1.user
- >>> a1.__dict__
- {'_sa_instance_state': <sqlalchemy.orm.state.InstanceState object at 0x1298d10>,
- 'user_id': 1,
- 'id': 1,
- 'user': None}
- >>> # we don't want user=None merged, remove it
- >>> del a1.user
- >>> a1 = session.merge(a1)
- >>> # success
- >>> session.commit()
-
-Expunging
----------
-
-Expunge removes an object from the Session, sending persistent instances to
-the detached state, and pending instances to the transient state:
-
-.. sourcecode:: python+sql
-
- session.expunge(obj1)
-
-To remove all items, call :meth:`~.Session.expunge_all`
-(this method was formerly known as ``clear()``).
-
-.. _session_expire:
-
-Refreshing / Expiring
----------------------
-
-:term:`Expiring` means that the database-persisted data held inside a series
-of object attributes is erased, in such a way that when those attributes
-are next accessed, a SQL query is emitted which will refresh that data from
-the database.
-
-When we talk about expiration of data we are usually talking about an object
-that is in the :term:`persistent` state. For example, if we load an object
-as follows::
-
- user = session.scalars(select(User).filter_by(name="user1").limit(1)).first()
-
-The above ``User`` object is persistent, and has a series of attributes
-present; if we were to look inside its ``__dict__``, we'd see that state
-loaded::
-
- >>> user.__dict__
- {
- 'id': 1, 'name': u'user1',
- '_sa_instance_state': <...>,
- }
-
-where ``id`` and ``name`` refer to those columns in the database.
-``_sa_instance_state`` is a non-database-persisted value used by SQLAlchemy
-internally (it refers to the :class:`.InstanceState` for the instance.
-While not directly relevant to this section, if we want to get at it,
-we should use the :func:`_sa.inspect` function to access it).
-
-At this point, the state in our ``User`` object matches that of the loaded
-database row. But upon expiring the object using a method such as
-:meth:`.Session.expire`, we see that the state is removed::
-
- >>> session.expire(user)
- >>> user.__dict__
- {'_sa_instance_state': <...>}
-
-We see that while the internal "state" still hangs around, the values which
-correspond to the ``id`` and ``name`` columns are gone. If we were to access
-one of these columns and are watching SQL, we'd see this:
-
-.. sourcecode:: pycon+sql
-
- >>> print(user.name)
- {execsql}SELECT user.id AS user_id, user.name AS user_name
- FROM user
- WHERE user.id = ?
- (1,)
- {stop}user1
-
-Above, upon accessing the expired attribute ``user.name``, the ORM initiated
-a :term:`lazy load` to retrieve the most recent state from the database,
-by emitting a SELECT for the user row to which this user refers. Afterwards,
-the ``__dict__`` is again populated::
-
- >>> user.__dict__
- {
- 'id': 1, 'name': u'user1',
- '_sa_instance_state': <...>,
- }
-
-.. note:: While we are peeking inside of ``__dict__`` in order to see a bit
- of what SQLAlchemy does with object attributes, we **should not modify**
- the contents of ``__dict__`` directly, at least as far as those attributes
- which the SQLAlchemy ORM is maintaining (other attributes outside of SQLA's
- realm are fine). This is because SQLAlchemy uses :term:`descriptors` in
- order to track the changes we make to an object, and when we modify ``__dict__``
- directly, the ORM won't be able to track that we changed something.
-
-Another key behavior of both :meth:`~.Session.expire` and :meth:`~.Session.refresh`
-is that all un-flushed changes on an object are discarded. That is,
-if we were to modify an attribute on our ``User``::
-
- >>> user.name = "user2"
-
-but then we call :meth:`~.Session.expire` without first calling :meth:`~.Session.flush`,
-our pending value of ``'user2'`` is discarded::
-
- >>> session.expire(user)
- >>> user.name
- 'user1'
-
-The :meth:`~.Session.expire` method can be used to mark as "expired" all ORM-mapped
-attributes for an instance::
-
- # expire all ORM-mapped attributes on obj1
- session.expire(obj1)
-
-it can also be passed a list of string attribute names, referring to specific
-attributes to be marked as expired::
-
- # expire only attributes obj1.attr1, obj1.attr2
- session.expire(obj1, ["attr1", "attr2"])
-
-The :meth:`.Session.expire_all` method allows us to essentially call
-:meth:`.Session.expire` on all objects contained within the :class:`.Session`
-at once::
-
- session.expire_all()
-
-The :meth:`~.Session.refresh` method has a similar interface, but instead
-of expiring, it emits an immediate SELECT for the object's row immediately::
-
- # reload all attributes on obj1
- session.refresh(obj1)
-
-:meth:`~.Session.refresh` also accepts a list of string attribute names,
-but unlike :meth:`~.Session.expire`, expects at least one name to
-be that of a column-mapped attribute::
-
- # reload obj1.attr1, obj1.attr2
- session.refresh(obj1, ["attr1", "attr2"])
-
-.. tip::
-
- An alternative method of refreshing which is often more flexible is to
- use the :ref:`orm_queryguide_populate_existing` feature of the ORM,
- available for :term:`2.0 style` queries with :func:`_sql.select` as well
- as from the :meth:`_orm.Query.populate_existing` method of :class:`_orm.Query`
- within :term:`1.x style` queries. Using this execution option,
- all of the ORM objects returned in the result set of the statement
- will be refreshed with data from the database::
-
- stmt = (
- select(User)
- .execution_options(populate_existing=True)
- .where((User.name.in_(["a", "b", "c"])))
- )
- for user in session.execute(stmt).scalars():
- print(user) # will be refreshed for those columns that came back from the query
-
- See :ref:`orm_queryguide_populate_existing` for further detail.
-
-
-What Actually Loads
-~~~~~~~~~~~~~~~~~~~
-
-The SELECT statement that's emitted when an object marked with :meth:`~.Session.expire`
-or loaded with :meth:`~.Session.refresh` varies based on several factors, including:
-
-* The load of expired attributes is triggered from **column-mapped attributes only**.
- While any kind of attribute can be marked as expired, including a
- :func:`_orm.relationship` - mapped attribute, accessing an expired :func:`_orm.relationship`
- attribute will emit a load only for that attribute, using standard
- relationship-oriented lazy loading. Column-oriented attributes, even if
- expired, will not load as part of this operation, and instead will load when
- any column-oriented attribute is accessed.
-
-* :func:`_orm.relationship`- mapped attributes will not load in response to
- expired column-based attributes being accessed.
-
-* Regarding relationships, :meth:`~.Session.refresh` is more restrictive than
- :meth:`.Session.expire` with regards to attributes that aren't column-mapped.
- Calling :meth:`.Session.refresh` and passing a list of names that only includes
- relationship-mapped attributes will actually raise an error.
- In any case, non-eager-loading :func:`_orm.relationship` attributes will not be
- included in any refresh operation.
-
-* :func:`_orm.relationship` attributes configured as "eager loading" via the
- :paramref:`_orm.relationship.lazy` parameter will load in the case of
- :meth:`~.Session.refresh`, if either no attribute names are specified, or
- if their names are included in the list of attributes to be
- refreshed.
-
-* Attributes that are configured as :func:`.deferred` will not normally load,
- during either the expired-attribute load or during a refresh.
- An unloaded attribute that's :func:`.deferred` instead loads on its own when directly
- accessed, or if part of a "group" of deferred attributes where an unloaded
- attribute in that group is accessed.
-
-* For expired attributes that are loaded on access, a joined-inheritance table
- mapping will emit a SELECT that typically only includes those tables for which
- unloaded attributes are present. The action here is sophisticated enough
- to load only the parent or child table, for example, if the subset of columns
- that were originally expired encompass only one or the other of those tables.
-
-* When :meth:`~.Session.refresh` is used on a joined-inheritance table mapping,
- the SELECT emitted will resemble that of when :meth:`.Session.query` is
- used on the target object's class. This is typically all those tables that
- are set up as part of the mapping.
-
-
-When to Expire or Refresh
-~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The :class:`.Session` uses the expiration feature automatically whenever
-the transaction referred to by the session ends. Meaning, whenever :meth:`.Session.commit`
-or :meth:`.Session.rollback` is called, all objects within the :class:`.Session`
-are expired, using a feature equivalent to that of the :meth:`.Session.expire_all`
-method. The rationale is that the end of a transaction is a
-demarcating point at which there is no more context available in order to know
-what the current state of the database is, as any number of other transactions
-may be affecting it. Only when a new transaction starts can we again have access
-to the current state of the database, at which point any number of changes
-may have occurred.
-
-.. sidebar:: Transaction Isolation
-
- Of course, most databases are capable of handling
- multiple transactions at once, even involving the same rows of data. When
- a relational database handles multiple transactions involving the same
- tables or rows, this is when the :term:`isolation` aspect of the database comes
- into play. The isolation behavior of different databases varies considerably
- and even on a single database can be configured to behave in different ways
- (via the so-called :term:`isolation level` setting). In that sense, the :class:`.Session`
- can't fully predict when the same SELECT statement, emitted a second time,
- will definitely return the data we already have, or will return new data.
- So as a best guess, it assumes that within the scope of a transaction, unless
- it is known that a SQL expression has been emitted to modify a particular row,
- there's no need to refresh a row unless explicitly told to do so.
-
-The :meth:`.Session.expire` and :meth:`.Session.refresh` methods are used in
-those cases when one wants to force an object to re-load its data from the
-database, in those cases when it is known that the current state of data
-is possibly stale. Reasons for this might include:
-
-* some SQL has been emitted within the transaction outside of the
- scope of the ORM's object handling, such as if a :meth:`_schema.Table.update` construct
- were emitted using the :meth:`.Session.execute` method;
-
-* if the application
- is attempting to acquire data that is known to have been modified in a
- concurrent transaction, and it is also known that the isolation rules in effect
- allow this data to be visible.
-
-The second bullet has the important caveat that "it is also known that the isolation rules in effect
-allow this data to be visible." This means that it cannot be assumed that an
-UPDATE that happened on another database connection will yet be visible here
-locally; in many cases, it will not. This is why if one wishes to use
-:meth:`.Session.expire` or :meth:`.Session.refresh` in order to view data between ongoing
-transactions, an understanding of the isolation behavior in effect is essential.
-
-.. seealso::
-
- :meth:`.Session.expire`
-
- :meth:`.Session.expire_all`
-
- :meth:`.Session.refresh`
-
- :ref:`orm_queryguide_populate_existing` - allows any ORM query
- to refresh objects as they would be loaded normally, refreshing
- all matching objects in the identity map against the results of a
- SELECT statement.
-
- :term:`isolation` - glossary explanation of isolation which includes links
- to Wikipedia.
-
- `The SQLAlchemy Session In-Depth <https://techspot.zzzeek.org/2012/11/14/pycon-canada-the-sqlalchemy-session-in-depth/>`_ - a video + slides with an in-depth discussion of the object
- lifecycle including the role of data expiration.
+++ /dev/null
-.. _mapper_version_counter:
-
-Configuring a Version Counter
-=============================
-
-The :class:`_orm.Mapper` supports management of a :term:`version id column`, which
-is a single table column that increments or otherwise updates its value
-each time an ``UPDATE`` to the mapped table occurs. This value is checked each
-time the ORM emits an ``UPDATE`` or ``DELETE`` against the row to ensure that
-the value held in memory matches the database value.
-
-.. warning::
-
- Because the versioning feature relies upon comparison of the **in memory**
- record of an object, the feature only applies to the :meth:`.Session.flush`
- process, where the ORM flushes individual in-memory rows to the database.
- It does **not** take effect when performing
- a multirow UPDATE or DELETE using :meth:`_query.Query.update` or :meth:`_query.Query.delete`
- methods, as these methods only emit an UPDATE or DELETE statement but otherwise
- do not have direct access to the contents of those rows being affected.
-
-The purpose of this feature is to detect when two concurrent transactions
-are modifying the same row at roughly the same time, or alternatively to provide
-a guard against the usage of a "stale" row in a system that might be re-using
-data from a previous transaction without refreshing (e.g. if one sets ``expire_on_commit=False``
-with a :class:`.Session`, it is possible to re-use the data from a previous
-transaction).
-
-.. topic:: Concurrent transaction updates
-
- When detecting concurrent updates within transactions, it is typically the
- case that the database's transaction isolation level is below the level of
- :term:`repeatable read`; otherwise, the transaction will not be exposed
- to a new row value created by a concurrent update which conflicts with
- the locally updated value. In this case, the SQLAlchemy versioning
- feature will typically not be useful for in-transaction conflict detection,
- though it still can be used for cross-transaction staleness detection.
-
- The database that enforces repeatable reads will typically either have locked the
- target row against a concurrent update, or is employing some form
- of multi version concurrency control such that it will emit an error
- when the transaction is committed. SQLAlchemy's version_id_col is an alternative
- which allows version tracking to occur for specific tables within a transaction
- that otherwise might not have this isolation level set.
-
- .. seealso::
-
- `Repeatable Read Isolation Level <https://www.postgresql.org/docs/current/static/transaction-iso.html#XACT-REPEATABLE-READ>`_ - PostgreSQL's implementation of repeatable read, including a description of the error condition.
-
-Simple Version Counting
------------------------
-
-The most straightforward way to track versions is to add an integer column
-to the mapped table, then establish it as the ``version_id_col`` within the
-mapper options::
-
- class User(Base):
- __tablename__ = "user"
-
- id = mapped_column(Integer, primary_key=True)
- version_id = mapped_column(Integer, nullable=False)
- name = mapped_column(String(50), nullable=False)
-
- __mapper_args__ = {"version_id_col": version_id}
-
-.. note:: It is **strongly recommended** that the ``version_id`` column
- be made NOT NULL. The versioning feature **does not support** a NULL
- value in the versioning column.
-
-Above, the ``User`` mapping tracks integer versions using the column
-``version_id``. When an object of type ``User`` is first flushed, the
-``version_id`` column will be given a value of "1". Then, an UPDATE
-of the table later on will always be emitted in a manner similar to the
-following:
-
-.. sourcecode:: sql
-
- UPDATE user SET version_id=:version_id, name=:name
- WHERE user.id = :user_id AND user.version_id = :user_version_id
- -- {"name": "new name", "version_id": 2, "user_id": 1, "user_version_id": 1}
-
-The above UPDATE statement is updating the row that not only matches
-``user.id = 1``, it also is requiring that ``user.version_id = 1``, where "1"
-is the last version identifier we've been known to use on this object.
-If a transaction elsewhere has modified the row independently, this version id
-will no longer match, and the UPDATE statement will report that no rows matched;
-this is the condition that SQLAlchemy tests, that exactly one row matched our
-UPDATE (or DELETE) statement. If zero rows match, that indicates our version
-of the data is stale, and a :exc:`.StaleDataError` is raised.
-
-.. _custom_version_counter:
-
-Custom Version Counters / Types
--------------------------------
-
-Other kinds of values or counters can be used for versioning. Common types include
-dates and GUIDs. When using an alternate type or counter scheme, SQLAlchemy
-provides a hook for this scheme using the ``version_id_generator`` argument,
-which accepts a version generation callable. This callable is passed the value of the current
-known version, and is expected to return the subsequent version.
-
-For example, if we wanted to track the versioning of our ``User`` class
-using a randomly generated GUID, we could do this (note that some backends
-support a native GUID type, but we illustrate here using a simple string)::
-
- import uuid
-
-
- class User(Base):
- __tablename__ = "user"
-
- id = mapped_column(Integer, primary_key=True)
- version_uuid = mapped_column(String(32), nullable=False)
- name = mapped_column(String(50), nullable=False)
-
- __mapper_args__ = {
- "version_id_col": version_uuid,
- "version_id_generator": lambda version: uuid.uuid4().hex,
- }
-
-The persistence engine will call upon ``uuid.uuid4()`` each time a
-``User`` object is subject to an INSERT or an UPDATE. In this case, our
-version generation function can disregard the incoming value of ``version``,
-as the ``uuid4()`` function
-generates identifiers without any prerequisite value. If we were using
-a sequential versioning scheme such as numeric or a special character system,
-we could make use of the given ``version`` in order to help determine the
-subsequent value.
-
-.. seealso::
-
- :ref:`custom_guid_type`
-
-.. _server_side_version_counter:
-
-Server Side Version Counters
-----------------------------
-
-The ``version_id_generator`` can also be configured to rely upon a value
-that is generated by the database. In this case, the database would need
-some means of generating new identifiers when a row is subject to an INSERT
-as well as with an UPDATE. For the UPDATE case, typically an update trigger
-is needed, unless the database in question supports some other native
-version identifier. The PostgreSQL database in particular supports a system
-column called `xmin <https://www.postgresql.org/docs/current/static/ddl-system-columns.html>`_
-which provides UPDATE versioning. We can make use
-of the PostgreSQL ``xmin`` column to version our ``User``
-class as follows::
-
- from sqlalchemy import FetchedValue
-
-
- class User(Base):
- __tablename__ = "user"
-
- id = mapped_column(Integer, primary_key=True)
- name = mapped_column(String(50), nullable=False)
- xmin = mapped_column("xmin", String, system=True, server_default=FetchedValue())
-
- __mapper_args__ = {"version_id_col": xmin, "version_id_generator": False}
-
-With the above mapping, the ORM will rely upon the ``xmin`` column for
-automatically providing the new value of the version id counter.
-
-.. topic:: creating tables that refer to system columns
-
- In the above scenario, as ``xmin`` is a system column provided by PostgreSQL,
- we use the ``system=True`` argument to mark it as a system-provided
- column, omitted from the ``CREATE TABLE`` statement. The datatype of this
- column is an internal PostgreSQL type called ``xid`` which acts mostly
- like a string, so we use the :class:`_types.String` datatype.
-
-
-The ORM typically does not actively fetch the values of database-generated
-values when it emits an INSERT or UPDATE, instead leaving these columns as
-"expired" and to be fetched when they are next accessed, unless the ``eager_defaults``
-:class:`_orm.Mapper` flag is set. However, when a
-server side version column is used, the ORM needs to actively fetch the newly
-generated value. This is so that the version counter is set up *before*
-any concurrent transaction may update it again. This fetching is also
-best done simultaneously within the INSERT or UPDATE statement using :term:`RETURNING`,
-otherwise if emitting a SELECT statement afterwards, there is still a potential
-race condition where the version counter may change before it can be fetched.
-
-When the target database supports RETURNING, an INSERT statement for our ``User`` class will look
-like this:
-
-.. sourcecode:: sql
-
- INSERT INTO "user" (name) VALUES (%(name)s) RETURNING "user".id, "user".xmin
- -- {'name': 'ed'}
-
-Where above, the ORM can acquire any newly generated primary key values along
-with server-generated version identifiers in one statement. When the backend
-does not support RETURNING, an additional SELECT must be emitted for **every**
-INSERT and UPDATE, which is much less efficient, and also introduces the possibility of
-missed version counters:
-
-.. sourcecode:: sql
-
- INSERT INTO "user" (name) VALUES (%(name)s)
- -- {'name': 'ed'}
-
- SELECT "user".version_id AS user_version_id FROM "user" where
- "user".id = :param_1
- -- {"param_1": 1}
-
-It is *strongly recommended* that server side version counters only be used
-when absolutely necessary and only on backends that support :term:`RETURNING`,
-currently PostgreSQL, Oracle, MariaDB 10.5, SQLite 3.35, and SQL Server.
-
-
-Programmatic or Conditional Version Counters
---------------------------------------------
-
-When ``version_id_generator`` is set to False, we can also programmatically
-(and conditionally) set the version identifier on our object in the same way
-we assign any other mapped attribute. Such as if we used our UUID example, but
-set ``version_id_generator`` to ``False``, we can set the version identifier
-at our choosing::
-
- import uuid
-
-
- class User(Base):
- __tablename__ = "user"
-
- id = mapped_column(Integer, primary_key=True)
- version_uuid = mapped_column(String(32), nullable=False)
- name = mapped_column(String(50), nullable=False)
-
- __mapper_args__ = {"version_id_col": version_uuid, "version_id_generator": False}
-
-
- u1 = User(name="u1", version_uuid=uuid.uuid4())
-
- session.add(u1)
-
- session.commit()
-
- u1.name = "u2"
- u1.version_uuid = uuid.uuid4()
-
- session.commit()
-
-We can update our ``User`` object without incrementing the version counter
-as well; the value of the counter will remain unchanged, and the UPDATE
-statement will still check against the previous value. This may be useful
-for schemes where only certain classes of UPDATE are sensitive to concurrency
-issues::
-
- # will leave version_uuid unchanged
- u1.name = "u3"
- session.commit()