Commits

Mike Bayer committed d95ac76 Merge

merge default

Comments (0)

Files changed (16)

 ^doc/build/output
 .pyc$
 .orig$
+.egg-info
     has been changed.  Thanks to Kumar McMillan for the patch.
     [ticket:1071]
     
+  - Repaired missing import in psycopg2._PGNumeric type when 
+    unknown numeric is received.
+
+  - psycopg2/pg8000 dialects now aware of REAL[], FLOAT[], 
+    DOUBLE_PRECISION[], NUMERIC[] return types without
+    raising an exception.
+    
 - oracle
   - Now using cx_oracle output converters so that the
     DBAPI returns natively the kinds of values we prefer:

doc/build/mappers.rst

     # custom selectable
     query.with_polymorphic([Engineer, Manager], employees.outerjoin(managers).outerjoin(engineers))
 
-:func:`~sqlalchemy.orm.query.Query.with_polymorphic` is also needed when you wish to add filter criterion that is specific to one or more subclasses, so that those columns are available to the WHERE clause:
+:func:`~sqlalchemy.orm.query.Query.with_polymorphic` is also needed
+when you wish to add filter criteria that are specific to one or more
+subclasses; It makes the subclasses' columns available to the WHERE clause:
 
 .. sourcecode:: python+sql
 

lib/sqlalchemy/dialects/postgresql/base.py

         CHAR, TEXT, FLOAT, NUMERIC, \
         DATE, BOOLEAN
 
+_DECIMAL_TYPES = (1700, 1231)
+_FLOAT_TYPES = (700, 701, 1021, 1022)
+
+
 class REAL(sqltypes.Float):
     __visit_name__ = "REAL"
 

lib/sqlalchemy/dialects/postgresql/pg8000.py

 from sqlalchemy import processors
 from sqlalchemy import types as sqltypes
 from sqlalchemy.dialects.postgresql.base import PGDialect, \
-                PGCompiler, PGIdentifierPreparer, PGExecutionContext
+                PGCompiler, PGIdentifierPreparer, PGExecutionContext,\
+                _DECIMAL_TYPES, _FLOAT_TYPES
 
 class _PGNumeric(sqltypes.Numeric):
     def result_processor(self, dialect, coltype):
         if self.asdecimal:
-            if coltype in (700, 701):
+            if coltype in _FLOAT_TYPES:
                 return processors.to_decimal_processor_factory(decimal.Decimal)
-            elif coltype == 1700:
+            elif coltype in _DECIMAL_TYPES:
                 # pg8000 returns Decimal natively for 1700
                 return None
             else:
                 raise exc.InvalidRequestError("Unknown PG numeric type: %d" % coltype)
         else:
-            if coltype in (700, 701):
+            if coltype in _FLOAT_TYPES:
                 # pg8000 returns float natively for 701
                 return None
-            elif coltype == 1700:
+            elif coltype in _DECIMAL_TYPES:
                 return processors.to_float
             else:
                 raise exc.InvalidRequestError("Unknown PG numeric type: %d" % coltype)

lib/sqlalchemy/dialects/postgresql/psycopg2.py

 import decimal
 import logging
 
-from sqlalchemy import util
+from sqlalchemy import util, exc
 from sqlalchemy import processors
 from sqlalchemy.engine import base, default
 from sqlalchemy.sql import expression
 from sqlalchemy import types as sqltypes
 from sqlalchemy.dialects.postgresql.base import PGDialect, PGCompiler, \
                                             PGIdentifierPreparer, PGExecutionContext, \
-                                            ENUM, ARRAY
+                                            ENUM, ARRAY, _DECIMAL_TYPES, _FLOAT_TYPES
 
 
 logger = logging.getLogger('sqlalchemy.dialects.postgresql')
 
     def result_processor(self, dialect, coltype):
         if self.asdecimal:
-            if coltype in (700, 701):
+            if coltype in _FLOAT_TYPES:
                 return processors.to_decimal_processor_factory(decimal.Decimal)
-            elif coltype == 1700:
+            elif coltype in _DECIMAL_TYPES:
                 # pg8000 returns Decimal natively for 1700
                 return None
             else:
                 raise exc.InvalidRequestError("Unknown PG numeric type: %d" % coltype)
         else:
-            if coltype in (700, 701):
+            if coltype in _FLOAT_TYPES:
                 # pg8000 returns float natively for 701
                 return None
-            elif coltype == 1700:
+            elif coltype in _DECIMAL_TYPES:
                 return processors.to_float
             else:
                 raise exc.InvalidRequestError("Unknown PG numeric type: %d" % coltype)

lib/sqlalchemy/ext/compiler.py

 
 The above compiler will prefix all INSERT statements with "some prefix" when compiled.
 
+Changing Compilation of Types
+=============================
+
+``compiler`` works for types, too, such as below where we implement the MS-SQL specific 'max' keyword for ``String``/``VARCHAR``::
+
+    @compiles(String, 'mssql')
+    @compiles(VARCHAR, 'mssql')
+    def compile_varchar(element, compiler, **kw):
+        if element.length == 'max':
+            return "VARCHAR('max')"
+        else:
+            return compiler.visit_VARCHAR(element, **kw)
+    
+    foo = Table('foo', metadata,
+        Column('data', VARCHAR('max'))
+    )
+
 Subclassing Guidelines
 ======================
 
   used with any expression class that represents a "standalone" SQL statement that
   can be passed directly to an ``execute()`` method.  It is already implicit 
   within ``DDLElement`` and ``FunctionElement``.
-  
+
 """
 
 def compiles(class_, *specs):

lib/sqlalchemy/orm/dependency.py

File contents unchanged.

lib/sqlalchemy/orm/interfaces.py

File contents unchanged.

lib/sqlalchemy/orm/unitofwork.py

File contents unchanged.

lib/sqlalchemy/sql/util.py

File contents unchanged.

lib/sqlalchemy/test/testing.py

                                     for column_values in rows])
     table.append_ddl_listener('after-create', onload)
 
+def provide_metadata(fn):
+    """Provides a bound MetaData object for a single test, 
+    drops it afterwards."""
+    def maybe(*args, **kw):
+        metadata = schema.MetaData(db)
+        context = dict(fn.func_globals)
+        context['metadata'] = metadata
+        # jython bug #1034
+        rebound = types.FunctionType(
+            fn.func_code, context, fn.func_name, fn.func_defaults,
+            fn.func_closure)
+        try:
+            return rebound(*args, **kw)
+        finally:
+            metadata.drop_all()
+    return function_named(maybe, fn.__name__)
+    
 def resolve_artifact_names(fn):
     """Decorator, augment function globals with tables and classes.
 

lib/sqlalchemy/topological.py

File contents unchanged.

test/base/test_dependency.py

File contents unchanged.

test/dialect/test_postgresql.py

             ).scalar()
             eq_(round_decimal(ret, 9), result)
     
-    
+    @testing.provide_metadata
+    def test_arrays(self):
+        t1 = Table('t', metadata, 
+            Column('x', postgresql.ARRAY(Float)),
+            Column('y', postgresql.ARRAY(postgresql.REAL)),
+            Column('z', postgresql.ARRAY(postgresql.DOUBLE_PRECISION)),
+            Column('q', postgresql.ARRAY(Numeric))
+        )
+        metadata.create_all()
+        t1.insert().execute(x=[5], y=[5], z=[6], q=[6.4])
+        row = t1.select().execute().first()
+        eq_(
+            row, 
+            ([5], [5], [6], [decimal.Decimal("6.4")])
+        )
         
 class EnumTest(TestBase, AssertsExecutionResults, AssertsCompiledSQL):
     __only_on__ = 'postgresql'
         else:
             exception_cls = eng.dialect.dbapi.ProgrammingError
         assert_raises(exception_cls, eng.execute, "show transaction isolation level")
-
-
+    
+    @testing.fails_on('+zxjdbc', 
+                        "psycopg2/pg8000 specific assertion")
+    @testing.fails_on('pypostgresql', 
+                        "psycopg2/pg8000 specific assertion")
+    def test_numeric_raise(self):
+        stmt = text("select cast('hi' as char) as hi", typemap={'hi':Numeric})
+        assert_raises(
+            exc.InvalidRequestError,
+            testing.db.execute, stmt
+        )
+        
 class TimezoneTest(TestBase):
     """Test timezone-aware datetimes.
 

test/engine/test_execute.py

     def teardown_class(cls):
         metadata.drop_all()
 
-    @testing.fails_on_everything_except('firebird', 'maxdb', 'sqlite', '+pyodbc', '+mxodbc', '+zxjdbc', 'mysql+oursql')
+    @testing.fails_on_everything_except('firebird', 'maxdb', 
+                                        'sqlite', '+pyodbc', 
+                                        '+mxodbc', '+zxjdbc', 'mysql+oursql')
     def test_raw_qmark(self):
         for conn in (testing.db, testing.db.connect()):
             conn.execute("insert into users (user_id, user_name) values (?, ?)", (1,"jack"))
     # pyformat is supported for mysql, but skipping because a few driver
     # versions have a bug that bombs out on this test. (1.2.2b3, 1.2.2c1, 1.2.2)
     @testing.skip_if(lambda: testing.against('mysql+mysqldb'), 'db-api flaky')
-    @testing.fails_on_everything_except('postgresql+psycopg2', 'postgresql+pypostgresql', 'mysql+mysqlconnector')
+    @testing.fails_on_everything_except('postgresql+psycopg2', 
+                                    'postgresql+pypostgresql', 'mysql+mysqlconnector')
     def test_raw_python(self):
         for conn in (testing.db, testing.db.connect()):
             conn.execute("insert into users (user_id, user_name) values (%(id)s, %(name)s)",
         global users, metadata
         metadata = MetaData(testing.db)
         users = Table('users', metadata,
-            Column('user_id', INT, primary_key = True),
+            Column('user_id', INT, primary_key=True, test_needs_autoincrement=True),
             Column('user_name', VARCHAR(20)),
         )
         metadata.create_all()
                 
             assert_stmts(compiled, stmts)
             assert_stmts(cursor, cursor_stmts)
-   
-    @testing.fails_on('mysql+oursql', 'oursql dialect has some extra steps here') 
+    
+    def test_options(self):
+        track = []
+        class TrackProxy(ConnectionProxy):
+            def __getattribute__(self, key):
+                fn = object.__getattribute__(self, key)
+                def go(*arg, **kw):
+                    track.append(fn.__name__)
+                    return fn(*arg, **kw)
+                return go
+        engine = engines.testing_engine(options={'proxy':TrackProxy()})
+        conn = engine.connect()
+        c2 = conn.execution_options(foo='bar')
+        eq_(c2._execution_options, {'foo':'bar'})
+        c2.execute(select([1]))
+        c3 = c2.execution_options(bar='bat')
+        eq_(c3._execution_options, {'foo':'bar', 'bar':'bat'})
+        eq_(track, ['execute', 'cursor_execute'])
+        
+        
     def test_transactional(self):
         track = []
         class TrackProxy(ConnectionProxy):