]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- Support has been improved for Postgresql reflection behavior on very old
authorMike Bayer <mike_mp@zzzcomputing.com>
Tue, 18 Feb 2014 23:35:23 +0000 (18:35 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Wed, 19 Feb 2014 21:02:29 +0000 (16:02 -0500)
(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.

Conflicts:
doc/build/changelog/changelog_09.rst
test/dialect/postgresql/test_types.py

doc/build/changelog/changelog_08.rst
lib/sqlalchemy/dialects/postgresql/base.py
test/dialect/postgresql/test_dialect.py
test/dialect/postgresql/test_query.py
test/dialect/postgresql/test_reflection.py
test/dialect/postgresql/test_types.py

index 39d58f5e3ac8679522febb9ff732c5087550e74a..6d9412545f9e1ea7906cb9f7135b8708e0e12934 100644 (file)
 .. changelog::
     :version: 0.8.5
 
+    .. 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
index 1eaa6f6e1f2aa66e8aba8e58f1de82e3e3c9ba74..a3f08e47bd234e0a519eadd644b59399f2718e3a 100644 (file)
@@ -1824,35 +1824,22 @@ class PGDialect(default.DefaultDialect):
         table_oid = self.get_table_oid(connection, table_name, schema,
                                        info_cache=kw.get('info_cache'))
 
-        if self.server_version_info < (8, 0):
-            # the shortcoming of this query is that it will
-            # not detect a PK constraint that has been renamed.
-            # This query was removed with #2291, however it was reported
-            # that the newer queries do not work with PG 7 so here
-            # it is restored when old PG versions are detected.
-            PK_SQL = """
-              SELECT attname FROM pg_attribute
-              WHERE attrelid = (
-                 SELECT indexrelid FROM pg_index i
-                 WHERE i.indrelid = :table_oid
-                 AND i.indisprimary = 't')
-              ORDER BY attnum
-            """
-        elif self.server_version_info < (8, 4):
-            # unnest() and generate_subscripts() both introduced in
-            # version 8.4
+        if self.server_version_info < (8, 4):
             PK_SQL = """
                 SELECT a.attname
                 FROM
                     pg_class t
                     join pg_index ix on t.oid = ix.indrelid
                     join pg_attribute a
-                        on t.oid=a.attrelid and a.attnum=ANY(ix.indkey)
+                        on t.oid=a.attrelid AND %s
                  WHERE
                   t.oid = :table_oid and ix.indisprimary = 't'
                 ORDER BY a.attnum
-            """
+            """ % self._pg_index_any("a.attnum", "ix.indkey")
+
         else:
+            # unnest() and generate_subscripts() both introduced in
+            # version 8.4
             PK_SQL = """
                 SELECT a.attname
                 FROM pg_attribute a JOIN (
@@ -1938,6 +1925,21 @@ class PGDialect(default.DefaultDialect):
             fkeys.append(fkey_d)
         return fkeys
 
+    def _pg_index_any(self, col, compare_to):
+        if self.server_version_info < (8, 1):
+            # http://www.postgresql.org/message-id/10279.1124395722@sss.pgh.pa.us
+            # "In CVS tip you could replace this with "attnum = ANY (indkey)".
+            # Unfortunately, most array support doesn't work on int2vector in
+            # pre-8.1 releases, so I think you're kinda stuck with the above
+            # for now.
+            # regards, tom lane"
+            return "(%s)" % " OR ".join(
+                        "%s[%d] = %s" % (compare_to, ind, col)
+                        for ind in range(0, 10)
+                    )
+        else:
+            return "%s = ANY(%s)" % (col, compare_to)
+
     @reflection.cache
     def get_indexes(self, connection, table_name, schema, **kw):
         table_oid = self.get_table_oid(connection, table_name, schema,
@@ -1950,14 +1952,14 @@ class PGDialect(default.DefaultDialect):
           SELECT
               i.relname as relname,
               ix.indisunique, ix.indexprs, ix.indpred,
-              a.attname, a.attnum, ix.indkey::varchar
+              a.attname, a.attnum, ix.indkey%s
           FROM
               pg_class t
                     join pg_index ix on t.oid = ix.indrelid
                     join pg_class i on i.oid=ix.indexrelid
                     left outer join
                         pg_attribute a
-                        on t.oid=a.attrelid and a.attnum=ANY(ix.indkey)
+                        on t.oid=a.attrelid and %s
           WHERE
               t.relkind = 'r'
               and t.oid = :table_oid
@@ -1965,7 +1967,10 @@ class PGDialect(default.DefaultDialect):
           ORDER BY
               t.relname,
               i.relname
-        """
+        """ % (
+                "::varchar" if self.server_version_info >= (8, 1) else "",
+                self._pg_index_any("a.attnum", "ix.indkey")
+            )
 
         t = sql.text(IDX_SQL, typemap={'attname': sqltypes.Unicode})
         c = connection.execute(t, table_oid=table_oid)
index 3d48230f3404e4f95205b0faa9a05853fc340d65..0cc2a75d6e653212e1c87c6b696499318bb4d9a0 100644 (file)
@@ -202,6 +202,7 @@ class MiscTest(fixtures.TestBase, AssertsExecutionResults, AssertsCompiledSQL):
                     : Numeric})
         assert_raises(exc.InvalidRequestError, testing.db.execute, stmt)
 
+    @testing.only_if("postgresql >= 8.2", "requires standard_conforming_strings")
     def test_serial_integer(self):
         for type_, expected in [
             (Integer, 'SERIAL'),
index ee5800db0fc95c7fcfe43a34facdd899d9d3c132..82b49d974b5180d7dc4e25c17b222fd1786716da 100644 (file)
@@ -70,6 +70,7 @@ class InsertTest(fixtures.TestBase, AssertsExecutionResults):
         metadata.create_all()
         self._assert_data_with_sequence(table, 'my_seq')
 
+    @testing.requires.returning
     def test_sequence_returning_insert(self):
         table = Table('testtable', metadata, Column('id', Integer,
                       Sequence('my_seq'), primary_key=True),
@@ -84,6 +85,7 @@ class InsertTest(fixtures.TestBase, AssertsExecutionResults):
         metadata.create_all()
         self._assert_data_autoincrement(table)
 
+    @testing.requires.returning
     def test_opt_sequence_returning_insert(self):
         table = Table('testtable', metadata, Column('id', Integer,
                       Sequence('my_seq', optional=True),
@@ -97,6 +99,7 @@ class InsertTest(fixtures.TestBase, AssertsExecutionResults):
         metadata.create_all()
         self._assert_data_autoincrement(table)
 
+    @testing.requires.returning
     def test_autoincrement_returning_insert(self):
         table = Table('testtable', metadata, Column('id', Integer,
                       primary_key=True), Column('data', String(30)))
@@ -592,8 +595,7 @@ class ServerSideCursorsTest(fixtures.TestBase, AssertsExecutionResults):
 
 class MatchTest(fixtures.TestBase, AssertsCompiledSQL):
 
-    __only_on__ = 'postgresql'
-    __excluded_on__ = ('postgresql', '<', (8, 3, 0)),
+    __only_on__ = 'postgresql >= 8.3'
 
     @classmethod
     def setup_class(cls):
index ccb4a48cd4f0a4738948e7e9c8a069d2459caba3..ea8a1a5e846b41067b8a529be2e9e855843a3455 100644 (file)
@@ -19,7 +19,7 @@ class DomainReflectionTest(fixtures.TestBase, AssertsExecutionResults):
 
     """Test PostgreSQL domains"""
 
-    __only_on__ = 'postgresql'
+    __only_on__ = 'postgresql > 8.2'
 
     @classmethod
     def setup_class(cls):
@@ -127,8 +127,8 @@ class DomainReflectionTest(fixtures.TestBase, AssertsExecutionResults):
 class ReflectionTest(fixtures.TestBase):
     __only_on__ = 'postgresql'
 
-    @testing.fails_if(('postgresql', '<', (8, 4)),
-            "newer query is bypassed due to unsupported SQL functions")
+    @testing.fails_if("postgresql < 8.4",
+                    "Better int2vector functions not available")
     @testing.provide_metadata
     def test_reflected_primary_key_order(self):
         meta1 = self.metadata
@@ -169,6 +169,8 @@ class ReflectionTest(fixtures.TestBase):
         eq_(
             t.c.x.server_default.arg.text, "'%s'::character varying" % ("abcd" * 40)
         )
+
+    @testing.fails_if("postgresql < 8.1", "schema name leaks in, not sure")
     @testing.provide_metadata
     def test_renamed_sequence_reflection(self):
         metadata = self.metadata
index f0bbe86abb1ad3b7e5cc0a32b3fb06ccbf58bd8c..60a15db3bbb125fb5986e12fcf935a49e6c89c37 100644 (file)
@@ -99,7 +99,7 @@ class FloatCoercionTest(fixtures.TablesTest, AssertsExecutionResults):
 
 class EnumTest(fixtures.TestBase, AssertsExecutionResults, AssertsCompiledSQL):
 
-    __only_on__ = 'postgresql'
+    __only_on__ = 'postgresql > 8.3'
     __dialect__ = postgresql.dialect()
 
     def test_compile(self):
@@ -917,7 +917,7 @@ class SpecialTypesTest(fixtures.TestBase, ComparesTables, AssertsCompiledSQL):
 class UUIDTest(fixtures.TestBase):
     """Test the bind/return values of the UUID type."""
 
-    __only_on__ = 'postgresql'
+    __only_on__ = 'postgresql >= 8.3'
 
     @testing.requires.python25
     @testing.fails_on('postgresql+zxjdbc',