summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormike bayer <mike_mp@zzzcomputing.com>2018-12-03 02:35:20 +0000
committerGerrit Code Review <gerrit@bbpush.zzzcomputing.com>2018-12-03 02:35:20 +0000
commitacf03df445fab52e89391f9cff29c65c7200ae5b (patch)
tree4c362be323b4e8513c10ca41480825013db89e52
parent5945da4e41370bc71a0d3f811ea128be5663db6e (diff)
parentf9c476740fcc1e95e4e7fee523c2f235831f9409 (diff)
downloadsqlalchemy-acf03df445fab52e89391f9cff29c65c7200ae5b.tar.gz
Merge "Fix PostgreSQL reflection of domains expressed as arrays" into rel_1_2
-rw-r--r--doc/build/changelog/unreleased_12/4377.rst7
-rw-r--r--lib/sqlalchemy/dialects/postgresql/base.py13
-rw-r--r--test/dialect/postgresql/test_reflection.py21
3 files changed, 36 insertions, 5 deletions
diff --git a/doc/build/changelog/unreleased_12/4377.rst b/doc/build/changelog/unreleased_12/4377.rst
new file mode 100644
index 000000000..9d3477efa
--- /dev/null
+++ b/doc/build/changelog/unreleased_12/4377.rst
@@ -0,0 +1,7 @@
+.. change::
+ :tag: bug, postgresql
+ :tickets: 4377, 4380
+
+ Fixed issue where reflection of a PostgreSQL domain that is expressed as an
+ array would fail to be recognized. Pull request courtesy Jakub Synowiec.
+
diff --git a/lib/sqlalchemy/dialects/postgresql/base.py b/lib/sqlalchemy/dialects/postgresql/base.py
index fe6eadb78..01e0e331c 100644
--- a/lib/sqlalchemy/dialects/postgresql/base.py
+++ b/lib/sqlalchemy/dialects/postgresql/base.py
@@ -2587,6 +2587,12 @@ class PGDialect(default.DefaultDialect):
def _get_column_info(self, name, format_type, default,
notnull, domains, enums, schema, comment):
+ def _handle_array_type(attype):
+ return (
+ attype.replace('[]', ''), # strip '[]' from integer[], etc.
+ attype.endswith('[]'),
+ )
+
# strip (*) from character varying(5), timestamp(5)
# with time zone, geometry(POLYGON), etc.
attype = re.sub(r'\(.*\)', '', format_type)
@@ -2594,11 +2600,11 @@ class PGDialect(default.DefaultDialect):
# strip quotes from case sensitive enum names
attype = re.sub(r'^"|"$', '', attype)
- # strip '[]' from integer[], etc.
- attype = attype.replace('[]', '')
+ # strip '[]' from integer[], etc. and check if an array
+ attype, is_array = _handle_array_type(attype)
nullable = not notnull
- is_array = format_type.endswith('[]')
+
charlen = re.search(r'\(([\d,]+)\)', format_type)
if charlen:
charlen = charlen.group(1)
@@ -2663,6 +2669,7 @@ class PGDialect(default.DefaultDialect):
elif attype in domains:
domain = domains[attype]
attype = domain['attype']
+ attype, is_array = _handle_array_type(attype)
# A table can't override whether the domain is nullable.
nullable = domain['nullable']
if domain['default'] and not default:
diff --git a/test/dialect/postgresql/test_reflection.py b/test/dialect/postgresql/test_reflection.py
index 70bc26e0b..416b56d41 100644
--- a/test/dialect/postgresql/test_reflection.py
+++ b/test/dialect/postgresql/test_reflection.py
@@ -13,7 +13,7 @@ from sqlalchemy import Table, Column, MetaData, Integer, String, \
from sqlalchemy import exc
import sqlalchemy as sa
from sqlalchemy.dialects.postgresql import base as postgresql
-from sqlalchemy.dialects.postgresql import ARRAY, INTERVAL, TSRANGE
+from sqlalchemy.dialects.postgresql import ARRAY, INTERVAL, INTEGER, TSRANGE
from sqlalchemy.dialects.postgresql import ExcludeConstraint
import re
@@ -176,7 +176,8 @@ class DomainReflectionTest(fixtures.TestBase, AssertsExecutionResults):
'CREATE DOMAIN testdomain INTEGER NOT NULL DEFAULT 42', \
'CREATE DOMAIN test_schema.testdomain INTEGER DEFAULT 0', \
"CREATE TYPE testtype AS ENUM ('test')", \
- 'CREATE DOMAIN enumdomain AS testtype':
+ 'CREATE DOMAIN enumdomain AS testtype', \
+ 'CREATE DOMAIN arraydomain AS INTEGER[]':
try:
con.execute(ddl)
except exc.DBAPIError as e:
@@ -192,6 +193,8 @@ class DomainReflectionTest(fixtures.TestBase, AssertsExecutionResults):
con.execute('CREATE TABLE enum_test (id integer, data enumdomain)')
+ con.execute('CREATE TABLE array_test (id integer, data arraydomain)')
+
@classmethod
def teardown_class(cls):
con = testing.db.connect()
@@ -203,6 +206,8 @@ class DomainReflectionTest(fixtures.TestBase, AssertsExecutionResults):
con.execute("DROP TABLE enum_test")
con.execute("DROP DOMAIN enumdomain")
con.execute("DROP TYPE testtype")
+ con.execute('DROP TABLE array_test')
+ con.execute('DROP DOMAIN arraydomain')
def test_table_is_reflected(self):
metadata = MetaData(testing.db)
@@ -227,6 +232,18 @@ class DomainReflectionTest(fixtures.TestBase, AssertsExecutionResults):
['test']
)
+ def test_array_domain_is_reflected(self):
+ metadata = MetaData(testing.db)
+ table = Table('array_test', metadata, autoload=True)
+ eq_(
+ table.c.data.type.__class__,
+ ARRAY
+ )
+ eq_(
+ table.c.data.type.item_type.__class__,
+ INTEGER
+ )
+
def test_table_is_reflected_test_schema(self):
metadata = MetaData(testing.db)
table = Table('testtable', metadata, autoload=True,