]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Reflect Oracle NUMBER(NULL, 0) as INTEGER
authorKent Bower <kb@retailarchitects.com>
Wed, 11 Apr 2018 21:21:26 +0000 (17:21 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Thu, 12 Apr 2018 22:41:59 +0000 (18:41 -0400)
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-Id: I4627febd46cab7085299c0a5700ee0f0bdca513c
Pull-request: https://github.com/zzzeek/sqlalchemy/pull/437

doc/build/changelog/unreleased_12/oracle_number.rst [new file with mode: 0644]
lib/sqlalchemy/dialects/oracle/base.py
test/dialect/oracle/test_reflection.py

diff --git a/doc/build/changelog/unreleased_12/oracle_number.rst b/doc/build/changelog/unreleased_12/oracle_number.rst
new file mode 100644 (file)
index 0000000..7d073e1
--- /dev/null
@@ -0,0 +1,7 @@
+.. change::
+    :tags: bug, oracle
+    :versions: 1.3.0b1
+
+    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.
index 44ab9e3bbd5df73d4a662a6bf7aeb639107b0cad..e55a9cbc6a030d0952e3fd1f5221997a20c4e359 100644 (file)
@@ -354,7 +354,7 @@ from sqlalchemy.sql import operators as sql_operators
 from sqlalchemy.sql.elements import quoted_name
 from sqlalchemy import types as sqltypes, schema as sa_schema
 from sqlalchemy.types import VARCHAR, NVARCHAR, CHAR, \
-    BLOB, CLOB, TIMESTAMP, FLOAT
+    BLOB, CLOB, TIMESTAMP, FLOAT, INTEGER
 from itertools import groupby
 
 RESERVED_WORDS = \
@@ -1414,7 +1414,10 @@ class OracleDialect(default.DefaultDialect):
             comment = row[7]
 
             if coltype == 'NUMBER':
-                coltype = NUMBER(precision, scale)
+                if precision is None and scale == 0:
+                    coltype = INTEGER()
+                else:
+                    coltype = NUMBER(precision, scale)
             elif coltype in ('VARCHAR2', 'NVARCHAR2', 'CHAR'):
                 coltype = self.ischema_names.get(coltype)(length)
             elif 'WITH TIME ZONE' in coltype:
index d09f12e6004c391b1ff5662581e2c46ade6e88bc..190fd9f38efabb537011d35ff7c84f3e39349eec 100644 (file)
@@ -1,7 +1,7 @@
 # coding: utf-8
 
 
-from sqlalchemy.testing import eq_
+from sqlalchemy.testing import eq_, is_
 from sqlalchemy import exc
 from sqlalchemy.sql import table
 from sqlalchemy.testing import fixtures, AssertsCompiledSQL
@@ -12,7 +12,8 @@ from sqlalchemy import Integer, Text, LargeBinary, Unicode, UniqueConstraint,\
     literal_column, VARCHAR, create_engine, Date, NVARCHAR, \
     ForeignKeyConstraint, Sequence, Float, DateTime, cast, UnicodeText, \
     union, except_, type_coerce, or_, outerjoin, DATE, NCHAR, outparam, \
-    PrimaryKeyConstraint, FLOAT
+    PrimaryKeyConstraint, FLOAT, INTEGER
+from sqlalchemy.dialects.oracle.base import NUMBER
 from sqlalchemy.testing import assert_raises
 from sqlalchemy.testing.engines import testing_engine
 from sqlalchemy.testing.schema import Table, Column
@@ -534,3 +535,46 @@ class DBLinkReflectionTest(fixtures.TestBase):
         eq_(list(t.primary_key), [t.c.id])
 
 
+class TypeReflectionTest(fixtures.TestBase):
+    __only_on__ = 'oracle'
+    __backend__ = True
+
+    @testing.provide_metadata
+    def _run_test(self, specs, attributes):
+        columns = [Column('c%i' % (i + 1), t[0]) for i, t in enumerate(specs)]
+        m = self.metadata
+        Table('oracle_types', m, *columns)
+        m.create_all()
+        m2 = MetaData(testing.db)
+        table = Table('oracle_types', m2, autoload=True)
+        for i, (reflected_col, spec) in enumerate(zip(table.c, specs)):
+            expected_spec = spec[1]
+            reflected_type = reflected_col.type
+            is_(type(reflected_type), type(expected_spec))
+            for attr in attributes:
+                eq_(
+                    getattr(reflected_type, attr),
+                    getattr(expected_spec, attr),
+                    "Column %s: Attribute %s value of %s does not "
+                    "match %s for type %s" % (
+                        "c%i" % (i + 1),
+                        attr,
+                        getattr(reflected_type, attr),
+                        getattr(expected_spec, attr),
+                        spec[0]
+                    )
+                )
+
+    def test_integer_types(self):
+        specs = [
+            (Integer, INTEGER(),),
+            (Numeric, INTEGER(),),
+        ]
+        self._run_test(specs, [])
+
+    def test_number_types(self):
+        specs = [
+            (Numeric(5, 2), NUMBER(5, 2),),
+            (NUMBER, NUMBER(),),
+        ]
+        self._run_test(specs, ['precision', 'scale'])