- now supports multi-column foreign keys
- fix to reflecting date/datetime columns
- NCHAR and NVARCHAR type support added
+ - Oracle:
+ - 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.
- Schema:
- added autoincrement=True to Column; will disable schema generation
of SERIAL/AUTO_INCREMENT/identity seq for postgres/mysql/mssql if
the appropriate negation operator if one is available.
- calling a negation on an "IN" or "IS" clause will result in
"NOT IN", "IS NOT" (as opposed to NOT (x IN y)).
+ - 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 [ticket:172]
- ORM:
- attribute tracking modified to be more intelligent about detecting
changes, particularly with mutable types. TypeEngine objects now
objects, but also generalizes type handling and "modified" object
checking to be more complete and extensible.
- a wide refactoring to "attribute loader" and "options" architectures.
- ColumnLoader and PropertyLoader define their loading behaivor via switchable
+ ColumnProperty and PropertyLoader define their loading behaivor via switchable
"strategies", and MapperOptions no longer use mapper/property copying
in order to function; they are instead propigated via QueryContext
and SelectionContext objects at query/instnaces time.
All of the copying of mappers and properties that was used to handle
- inheritance as well as options() has been removed and the structure
- of mappers and properties is much simpler and clearly laid out.
+ inheritance as well as options() has been removed; the structure
+ of mappers and properties is simpler than before and clearly laid out.
- related to the mapper/property overhaul, internal refactoring to
mapper instances() method to use a SelectionContext object to track
state during the operation.
self.typemap.setdefault(func.name, func.type)
if not self.apply_function_parens(func):
self.strings[func] = ".".join(func.packagenames + [func.name])
+ self.froms[func] = self.strings[func]
else:
self.strings[func] = ".".join(func.packagenames + [func.name]) + "(" + string.join([self.get_str(c) for c in func.clauses], ', ') + ")"
+ self.froms[func] = self.strings[func]
def visit_compound_select(self, cs):
text = string.join([self.get_str(c) for c in cs.selects], " " + cs.keyword + " ")
def _process_from_dict(self, data, asfrom):
super(CalculatedClause, self)._process_from_dict(data, asfrom)
# this helps a Select object get the engine from us
- data.setdefault(self, self)
+ if asfrom:
+ data.setdefault(self, self)
def copy_container(self):
clauses = [clause.copy_container() for clause in self.clauses]
return CalculatedClause(type=self.type, engine=self._engine, *clauses)
return self.type
-class Function(CalculatedClause):
+class Function(CalculatedClause, FromClause):
"""describes a SQL function. extends CalculatedClause turn the "clauselist" into function
arguments, also adds a "packagenames" argument"""
def __init__(self, name, *clauses, **kwargs):
if e is not None:
self._engine = e
return e
+ # look through the columns (largely synomous with looking
+ # through the FROMs except in the case of CalculatedClause/Function)
+ for cc in self._raw_columns:
+ for c in cc.columns:
+ if getattr(c, 'table', None) is self:
+ continue
+ e = c.engine
+ if e is not None:
+ self._engine = e
+ return e
return None
class UpdateBase(ClauseElement):
r = db.execute('select user_name from query_users', {}).fetchone()
self.assertEqual(len(r), 1)
r.close()
+
+ def test_functions(self):
+ x = testbase.db.func.current_date().execute().scalar()
+ y = testbase.db.func.current_date().select().execute().scalar()
+ z = testbase.db.func.current_date().scalar()
+ assert x == y == z
+
+ @testbase.supported('postgres')
+ def test_functions_with_cols(self):
+ x = testbase.db.func.current_date().execute().scalar()
+ y = testbase.db.func.current_date().select().execute().scalar()
+ z = testbase.db.func.current_date().scalar()
+ w = select(['*'], from_obj=[testbase.db.func.current_date()]).scalar()
+
+ # construct a column-based FROM object out of a function, like in [ticket:172]
+ s = select([column('date', type=DateTime)], from_obj=[testbase.db.func.current_date()])
+ q = s.execute().fetchone()[s.c.date]
+ r = s.alias('datequery').select().scalar()
+
+ assert x == y == z == w == q == r
def test_column_order_with_simple_query(self):
# should return values in column definition order