From aaec1bdedfc73ead3aef3a3e4d835a8df339e2dd Mon Sep 17 00:00:00 2001 From: Federico Caselli Date: Sun, 19 Apr 2020 20:09:39 +0200 Subject: Support `ARRAY` of `Enum`, `JSON` or `JSONB` Added support for columns or type :class:`.ARRAY` of :class:`.Enum`, :class:`.JSON` or :class:`_postgresql.JSONB` in PostgreSQL. Previously a workaround was required in these use cases. Raise an explicit :class:`.exc.CompileError` when adding a table with a column of type :class:`.ARRAY` of :class:`.Enum` configured with :paramref:`.Enum.native_enum` set to ``False`` when :paramref:`.Enum.create_constraint` is not set to ``False`` Fixes: #5265 Fixes: #5266 Change-Id: I83a2d20a599232b7066d0839f3e55ff8b78cd8fc --- lib/sqlalchemy/dialects/postgresql/array.py | 53 ++++++++++++++++++++++------- 1 file changed, 41 insertions(+), 12 deletions(-) (limited to 'lib/sqlalchemy/dialects/postgresql/array.py') diff --git a/lib/sqlalchemy/dialects/postgresql/array.py b/lib/sqlalchemy/dialects/postgresql/array.py index a3537ba60..84fbd2e50 100644 --- a/lib/sqlalchemy/dialects/postgresql/array.py +++ b/lib/sqlalchemy/dialects/postgresql/array.py @@ -5,19 +5,14 @@ # This module is part of SQLAlchemy and is released under # the MIT License: http://www.opensource.org/licenses/mit-license.php -from .base import colspecs -from .base import ischema_names +import re + from ... import types as sqltypes +from ... import util from ...sql import expression from ...sql import operators -try: - from uuid import UUID as _python_UUID # noqa -except ImportError: - _python_UUID = None - - def Any(other, arrexpr, operator=operators.eq): """A synonym for the :meth:`.ARRAY.Comparator.any` method. @@ -318,6 +313,25 @@ class ARRAY(sqltypes.ARRAY): for x in arr ) + @util.memoized_property + def _require_cast(self): + return self._against_native_enum or isinstance( + self.item_type, sqltypes.JSON + ) + + @util.memoized_property + def _against_native_enum(self): + return ( + isinstance(self.item_type, sqltypes.Enum) + and self.item_type.native_enum + ) + + def bind_expression(self, bindvalue): + if self._require_cast: + return expression.cast(bindvalue, self) + else: + return bindvalue + def bind_processor(self, dialect): item_proc = self.item_type.dialect_impl(dialect).bind_processor( dialect @@ -349,8 +363,23 @@ class ARRAY(sqltypes.ARRAY): tuple if self.as_tuple else list, ) - return process - + if self._against_native_enum: + super_rp = process + + def handle_raw_string(value): + inner = re.match(r"^{(.*)}$", value).group(1) + return inner.split(",") if inner else [] + + def process(value): + if value is None: + return value + # isinstance(value, util.string_types) is required to handle + # the # case where a TypeDecorator for and Array of Enum is + # used like was required in sa < 1.3.17 + return super_rp( + handle_raw_string(value) + if isinstance(value, util.string_types) + else value + ) -colspecs[sqltypes.ARRAY] = ARRAY -ischema_names["_array"] = ARRAY + return process -- cgit v1.2.1