summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/build/changelog/unreleased_12/4506.rst8
-rw-r--r--doc/build/dialects/oracle.rst5
-rw-r--r--lib/sqlalchemy/dialects/oracle/__init__.py2
-rw-r--r--lib/sqlalchemy/dialects/oracle/base.py4
-rw-r--r--test/dialect/oracle/test_types.py30
5 files changed, 40 insertions, 9 deletions
diff --git a/doc/build/changelog/unreleased_12/4506.rst b/doc/build/changelog/unreleased_12/4506.rst
new file mode 100644
index 000000000..2fa987490
--- /dev/null
+++ b/doc/build/changelog/unreleased_12/4506.rst
@@ -0,0 +1,8 @@
+.. change::
+ :tags: bug, oracle
+ :tickets: 4506
+
+ Added support for reflection of the :class:`.NCHAR` datatype to the Oracle
+ dialect, and added :class:`.NCHAR` to the list of types exported by the
+ Oracle dialect.
+
diff --git a/doc/build/dialects/oracle.rst b/doc/build/dialects/oracle.rst
index f8e9f891a..7bb0d8c23 100644
--- a/doc/build/dialects/oracle.rst
+++ b/doc/build/dialects/oracle.rst
@@ -14,10 +14,13 @@ they originate from :mod:`sqlalchemy.types` or from the local dialect::
from sqlalchemy.dialects.oracle import \
BFILE, BLOB, CHAR, CLOB, DATE, \
- DOUBLE_PRECISION, FLOAT, INTERVAL, LONG, NCLOB, \
+ DOUBLE_PRECISION, FLOAT, INTERVAL, LONG, NCLOB, NCHAR, \
NUMBER, NVARCHAR, NVARCHAR2, RAW, TIMESTAMP, VARCHAR, \
VARCHAR2
+.. versionadded:: 1.2.19 Added :class:`.NCHAR` to the list of datatypes
+ exported by the Oracle dialect.
+
Types which are specific to Oracle, or have Oracle-specific
construction arguments, are as follows:
diff --git a/lib/sqlalchemy/dialects/oracle/__init__.py b/lib/sqlalchemy/dialects/oracle/__init__.py
index 1db7dba4c..9f1e15be4 100644
--- a/lib/sqlalchemy/dialects/oracle/__init__.py
+++ b/lib/sqlalchemy/dialects/oracle/__init__.py
@@ -19,6 +19,7 @@ from .base import DOUBLE_PRECISION
from .base import FLOAT
from .base import INTERVAL
from .base import LONG
+from .base import NCHAR
from .base import NCLOB
from .base import NUMBER
from .base import NVARCHAR
@@ -36,6 +37,7 @@ __all__ = (
"VARCHAR",
"NVARCHAR",
"CHAR",
+ "NCHAR",
"DATE",
"NUMBER",
"BLOB",
diff --git a/lib/sqlalchemy/dialects/oracle/base.py b/lib/sqlalchemy/dialects/oracle/base.py
index 95a6c1d21..9f0b23e12 100644
--- a/lib/sqlalchemy/dialects/oracle/base.py
+++ b/lib/sqlalchemy/dialects/oracle/base.py
@@ -364,6 +364,7 @@ from ...types import CHAR
from ...types import CLOB
from ...types import FLOAT
from ...types import INTEGER
+from ...types import NCHAR
from ...types import NVARCHAR
from ...types import TIMESTAMP
from ...types import VARCHAR
@@ -523,6 +524,7 @@ ischema_names = {
"VARCHAR2": VARCHAR,
"NVARCHAR2": NVARCHAR,
"CHAR": CHAR,
+ "NCHAR": NCHAR,
"DATE": DATE,
"NUMBER": NUMBER,
"BLOB": BLOB,
@@ -1514,7 +1516,7 @@ class OracleDialect(default.DefaultDialect):
elif coltype == "FLOAT":
# TODO: support "precision" here as "binary_precision"
coltype = FLOAT()
- elif coltype in ("VARCHAR2", "NVARCHAR2", "CHAR"):
+ elif coltype in ("VARCHAR2", "NVARCHAR2", "CHAR", "NCHAR"):
coltype = self.ischema_names.get(coltype)(length)
elif "WITH TIME ZONE" in coltype:
coltype = TIMESTAMP(timezone=True)
diff --git a/test/dialect/oracle/test_types.py b/test/dialect/oracle/test_types.py
index 5af5459ff..ae673b1fa 100644
--- a/test/dialect/oracle/test_types.py
+++ b/test/dialect/oracle/test_types.py
@@ -782,23 +782,37 @@ class TypesTest(fixtures.TestBase):
@testing.provide_metadata
def test_reflect_nvarchar(self):
metadata = self.metadata
- Table("tnv", metadata, Column("data", sqltypes.NVARCHAR(255)))
+ Table(
+ "tnv",
+ metadata,
+ Column("nv_data", sqltypes.NVARCHAR(255)),
+ Column("c_data", sqltypes.NCHAR(20)),
+ )
metadata.create_all()
m2 = MetaData(testing.db)
t2 = Table("tnv", m2, autoload=True)
- assert isinstance(t2.c.data.type, sqltypes.NVARCHAR)
+ assert isinstance(t2.c.nv_data.type, sqltypes.NVARCHAR)
+ assert isinstance(t2.c.c_data.type, sqltypes.NCHAR)
if testing.against("oracle+cx_oracle"):
assert isinstance(
- t2.c.data.type.dialect_impl(testing.db.dialect),
+ t2.c.nv_data.type.dialect_impl(testing.db.dialect),
+ cx_oracle._OracleUnicodeStringNCHAR,
+ )
+
+ assert isinstance(
+ t2.c.c_data.type.dialect_impl(testing.db.dialect),
cx_oracle._OracleUnicodeStringNCHAR,
)
data = u("m’a réveillé.")
- t2.insert().execute(data=data)
- res = t2.select().execute().first()["data"]
- eq_(res, data)
- assert isinstance(res, util.text_type)
+ with testing.db.connect() as conn:
+ conn.execute(t2.insert(), dict(nv_data=data, c_data=data))
+ nv_data, c_data = conn.execute(t2.select()).first()
+ eq_(nv_data, data)
+ eq_(c_data, data + (" " * 7)) # char is space padded
+ assert isinstance(nv_data, util.text_type)
+ assert isinstance(c_data, util.text_type)
@testing.provide_metadata
def test_reflect_unicode_no_nvarchar(self):
@@ -830,6 +844,7 @@ class TypesTest(fixtures.TestBase):
Column("c1", VARCHAR(50)),
Column("c2", NVARCHAR(250)),
Column("c3", CHAR(200)),
+ Column("c4", NCHAR(180)),
)
t1.create()
m2 = MetaData(testing.db)
@@ -837,6 +852,7 @@ class TypesTest(fixtures.TestBase):
eq_(t2.c.c1.type.length, 50)
eq_(t2.c.c2.type.length, 250)
eq_(t2.c.c3.type.length, 200)
+ eq_(t2.c.c4.type.length, 180)
@testing.provide_metadata
def test_long_type(self):