]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- Function objects know what to do in a FROM clause now. their
authorMike Bayer <mike_mp@zzzcomputing.com>
Tue, 3 Oct 2006 23:00:04 +0000 (23:00 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Tue, 3 Oct 2006 23:00:04 +0000 (23:00 +0000)
    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].  generally only postgres understands the
    syntax (and possibly oracle).

CHANGES
lib/sqlalchemy/ansisql.py
lib/sqlalchemy/sql.py
test/sql/query.py

diff --git a/CHANGES b/CHANGES
index 99e2d7a967336015c9b6355568070f390b1f3502..138791cc1cc82eb8497a95bd50d18743b04c49e3 100644 (file)
--- a/CHANGES
+++ b/CHANGES
     - 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.
index a687fe70552d3b8635b588a7e103ce80a71f42ee..6165192cde8a2a2017da9637a175a8ec750112c8 100644 (file)
@@ -239,8 +239,10 @@ class ANSICompiler(sql.Compiled):
             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 + " ")
index 89688cbfe302466d5a1d2e5e6b3c5f3dade2ab35..a07536bc9c4a9d4af27a64e1d0a1fe8315b06349 100644 (file)
@@ -928,7 +928,8 @@ class CalculatedClause(ClauseList, ColumnElement):
     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)
@@ -948,7 +949,7 @@ class CalculatedClause(ClauseList, ColumnElement):
         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):
@@ -1567,6 +1568,16 @@ class Select(SelectBaseMixin, FromClause):
             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):
index 75b1cea5cf7409acbb0c1e07b45bfd729bf4a0aa..2c92a4932242250f5cd308e6571db8fbbef50ede 100644 (file)
@@ -188,6 +188,26 @@ class QueryTest(PersistTest):
         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