You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					328 lines
				
				10 KiB
			
		
		
			
		
	
	
					328 lines
				
				10 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								# postgresql/json.py
							 | 
						||
| 
								 | 
							
								# Copyright (C) 2005-2022 the SQLAlchemy authors and contributors
							 | 
						||
| 
								 | 
							
								# <see AUTHORS file>
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								# This module is part of SQLAlchemy and is released under
							 | 
						||
| 
								 | 
							
								# the MIT License: https://www.opensource.org/licenses/mit-license.php
							 | 
						||
| 
								 | 
							
								from __future__ import absolute_import
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								from ... import types as sqltypes
							 | 
						||
| 
								 | 
							
								from ... import util
							 | 
						||
| 
								 | 
							
								from ...sql import operators
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								__all__ = ("JSON", "JSONB")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								idx_precedence = operators._PRECEDENCE[operators.json_getitem_op]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								ASTEXT = operators.custom_op(
							 | 
						||
| 
								 | 
							
								    "->>",
							 | 
						||
| 
								 | 
							
								    precedence=idx_precedence,
							 | 
						||
| 
								 | 
							
								    natural_self_precedent=True,
							 | 
						||
| 
								 | 
							
								    eager_grouping=True,
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								JSONPATH_ASTEXT = operators.custom_op(
							 | 
						||
| 
								 | 
							
								    "#>>",
							 | 
						||
| 
								 | 
							
								    precedence=idx_precedence,
							 | 
						||
| 
								 | 
							
								    natural_self_precedent=True,
							 | 
						||
| 
								 | 
							
								    eager_grouping=True,
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								HAS_KEY = operators.custom_op(
							 | 
						||
| 
								 | 
							
								    "?",
							 | 
						||
| 
								 | 
							
								    precedence=idx_precedence,
							 | 
						||
| 
								 | 
							
								    natural_self_precedent=True,
							 | 
						||
| 
								 | 
							
								    eager_grouping=True,
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								HAS_ALL = operators.custom_op(
							 | 
						||
| 
								 | 
							
								    "?&",
							 | 
						||
| 
								 | 
							
								    precedence=idx_precedence,
							 | 
						||
| 
								 | 
							
								    natural_self_precedent=True,
							 | 
						||
| 
								 | 
							
								    eager_grouping=True,
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								HAS_ANY = operators.custom_op(
							 | 
						||
| 
								 | 
							
								    "?|",
							 | 
						||
| 
								 | 
							
								    precedence=idx_precedence,
							 | 
						||
| 
								 | 
							
								    natural_self_precedent=True,
							 | 
						||
| 
								 | 
							
								    eager_grouping=True,
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								CONTAINS = operators.custom_op(
							 | 
						||
| 
								 | 
							
								    "@>",
							 | 
						||
| 
								 | 
							
								    precedence=idx_precedence,
							 | 
						||
| 
								 | 
							
								    natural_self_precedent=True,
							 | 
						||
| 
								 | 
							
								    eager_grouping=True,
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								CONTAINED_BY = operators.custom_op(
							 | 
						||
| 
								 | 
							
								    "<@",
							 | 
						||
| 
								 | 
							
								    precedence=idx_precedence,
							 | 
						||
| 
								 | 
							
								    natural_self_precedent=True,
							 | 
						||
| 
								 | 
							
								    eager_grouping=True,
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class JSONPathType(sqltypes.JSON.JSONPathType):
							 | 
						||
| 
								 | 
							
								    def bind_processor(self, dialect):
							 | 
						||
| 
								 | 
							
								        super_proc = self.string_bind_processor(dialect)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        def process(value):
							 | 
						||
| 
								 | 
							
								            assert isinstance(value, util.collections_abc.Sequence)
							 | 
						||
| 
								 | 
							
								            tokens = [util.text_type(elem) for elem in value]
							 | 
						||
| 
								 | 
							
								            value = "{%s}" % (", ".join(tokens))
							 | 
						||
| 
								 | 
							
								            if super_proc:
							 | 
						||
| 
								 | 
							
								                value = super_proc(value)
							 | 
						||
| 
								 | 
							
								            return value
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return process
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def literal_processor(self, dialect):
							 | 
						||
| 
								 | 
							
								        super_proc = self.string_literal_processor(dialect)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        def process(value):
							 | 
						||
| 
								 | 
							
								            assert isinstance(value, util.collections_abc.Sequence)
							 | 
						||
| 
								 | 
							
								            tokens = [util.text_type(elem) for elem in value]
							 | 
						||
| 
								 | 
							
								            value = "{%s}" % (", ".join(tokens))
							 | 
						||
| 
								 | 
							
								            if super_proc:
							 | 
						||
| 
								 | 
							
								                value = super_proc(value)
							 | 
						||
| 
								 | 
							
								            return value
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return process
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class JSON(sqltypes.JSON):
							 | 
						||
| 
								 | 
							
								    """Represent the PostgreSQL JSON type.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :class:`_postgresql.JSON` is used automatically whenever the base
							 | 
						||
| 
								 | 
							
								    :class:`_types.JSON` datatype is used against a PostgreSQL backend,
							 | 
						||
| 
								 | 
							
								    however base :class:`_types.JSON` datatype does not provide Python
							 | 
						||
| 
								 | 
							
								    accessors for PostgreSQL-specific comparison methods such as
							 | 
						||
| 
								 | 
							
								    :meth:`_postgresql.JSON.Comparator.astext`; additionally, to use
							 | 
						||
| 
								 | 
							
								    PostgreSQL ``JSONB``, the :class:`_postgresql.JSONB` datatype should
							 | 
						||
| 
								 | 
							
								    be used explicitly.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    .. seealso::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :class:`_types.JSON` - main documentation for the generic
							 | 
						||
| 
								 | 
							
								        cross-platform JSON datatype.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    The operators provided by the PostgreSQL version of :class:`_types.JSON`
							 | 
						||
| 
								 | 
							
								    include:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    * Index operations (the ``->`` operator)::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        data_table.c.data['some key']
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        data_table.c.data[5]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    * Index operations returning text (the ``->>`` operator)::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        data_table.c.data['some key'].astext == 'some value'
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      Note that equivalent functionality is available via the
							 | 
						||
| 
								 | 
							
								      :attr:`.JSON.Comparator.as_string` accessor.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    * Index operations with CAST
							 | 
						||
| 
								 | 
							
								      (equivalent to ``CAST(col ->> ['some key'] AS <type>)``)::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        data_table.c.data['some key'].astext.cast(Integer) == 5
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      Note that equivalent functionality is available via the
							 | 
						||
| 
								 | 
							
								      :attr:`.JSON.Comparator.as_integer` and similar accessors.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    * Path index operations (the ``#>`` operator)::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        data_table.c.data[('key_1', 'key_2', 5, ..., 'key_n')]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    * Path index operations returning text (the ``#>>`` operator)::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        data_table.c.data[('key_1', 'key_2', 5, ..., 'key_n')].astext == 'some value'
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    .. versionchanged:: 1.1  The :meth:`_expression.ColumnElement.cast`
							 | 
						||
| 
								 | 
							
								       operator on
							 | 
						||
| 
								 | 
							
								       JSON objects now requires that the :attr:`.JSON.Comparator.astext`
							 | 
						||
| 
								 | 
							
								       modifier be called explicitly, if the cast works only from a textual
							 | 
						||
| 
								 | 
							
								       string.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Index operations return an expression object whose type defaults to
							 | 
						||
| 
								 | 
							
								    :class:`_types.JSON` by default,
							 | 
						||
| 
								 | 
							
								    so that further JSON-oriented instructions
							 | 
						||
| 
								 | 
							
								    may be called upon the result type.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Custom serializers and deserializers are specified at the dialect level,
							 | 
						||
| 
								 | 
							
								    that is using :func:`_sa.create_engine`.  The reason for this is that when
							 | 
						||
| 
								 | 
							
								    using psycopg2, the DBAPI only allows serializers at the per-cursor
							 | 
						||
| 
								 | 
							
								    or per-connection level.   E.g.::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        engine = create_engine("postgresql://scott:tiger@localhost/test",
							 | 
						||
| 
								 | 
							
								                                json_serializer=my_serialize_fn,
							 | 
						||
| 
								 | 
							
								                                json_deserializer=my_deserialize_fn
							 | 
						||
| 
								 | 
							
								                        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    When using the psycopg2 dialect, the json_deserializer is registered
							 | 
						||
| 
								 | 
							
								    against the database using ``psycopg2.extras.register_default_json``.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    .. seealso::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :class:`_types.JSON` - Core level JSON type
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :class:`_postgresql.JSONB`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    .. versionchanged:: 1.1 :class:`_postgresql.JSON` is now a PostgreSQL-
							 | 
						||
| 
								 | 
							
								       specific specialization of the new :class:`_types.JSON` type.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    """  # noqa
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    astext_type = sqltypes.Text()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __init__(self, none_as_null=False, astext_type=None):
							 | 
						||
| 
								 | 
							
								        """Construct a :class:`_types.JSON` type.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :param none_as_null: if True, persist the value ``None`` as a
							 | 
						||
| 
								 | 
							
								         SQL NULL value, not the JSON encoding of ``null``.   Note that
							 | 
						||
| 
								 | 
							
								         when this flag is False, the :func:`.null` construct can still
							 | 
						||
| 
								 | 
							
								         be used to persist a NULL value::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								             from sqlalchemy import null
							 | 
						||
| 
								 | 
							
								             conn.execute(table.insert(), data=null())
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								         .. versionchanged:: 0.9.8 - Added ``none_as_null``, and :func:`.null`
							 | 
						||
| 
								 | 
							
								            is now supported in order to persist a NULL value.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								         .. seealso::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								              :attr:`_types.JSON.NULL`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :param astext_type: the type to use for the
							 | 
						||
| 
								 | 
							
								         :attr:`.JSON.Comparator.astext`
							 | 
						||
| 
								 | 
							
								         accessor on indexed attributes.  Defaults to :class:`_types.Text`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								         .. versionadded:: 1.1
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        super(JSON, self).__init__(none_as_null=none_as_null)
							 | 
						||
| 
								 | 
							
								        if astext_type is not None:
							 | 
						||
| 
								 | 
							
								            self.astext_type = astext_type
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    class Comparator(sqltypes.JSON.Comparator):
							 | 
						||
| 
								 | 
							
								        """Define comparison operations for :class:`_types.JSON`."""
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        @property
							 | 
						||
| 
								 | 
							
								        def astext(self):
							 | 
						||
| 
								 | 
							
								            """On an indexed expression, use the "astext" (e.g. "->>")
							 | 
						||
| 
								 | 
							
								            conversion when rendered in SQL.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            E.g.::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                select(data_table.c.data['some key'].astext)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            .. seealso::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                :meth:`_expression.ColumnElement.cast`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            """
							 | 
						||
| 
								 | 
							
								            if isinstance(self.expr.right.type, sqltypes.JSON.JSONPathType):
							 | 
						||
| 
								 | 
							
								                return self.expr.left.operate(
							 | 
						||
| 
								 | 
							
								                    JSONPATH_ASTEXT,
							 | 
						||
| 
								 | 
							
								                    self.expr.right,
							 | 
						||
| 
								 | 
							
								                    result_type=self.type.astext_type,
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                return self.expr.left.operate(
							 | 
						||
| 
								 | 
							
								                    ASTEXT, self.expr.right, result_type=self.type.astext_type
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    comparator_factory = Comparator
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class JSONB(JSON):
							 | 
						||
| 
								 | 
							
								    """Represent the PostgreSQL JSONB type.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    The :class:`_postgresql.JSONB` type stores arbitrary JSONB format data,
							 | 
						||
| 
								 | 
							
								    e.g.::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        data_table = Table('data_table', metadata,
							 | 
						||
| 
								 | 
							
								            Column('id', Integer, primary_key=True),
							 | 
						||
| 
								 | 
							
								            Column('data', JSONB)
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        with engine.connect() as conn:
							 | 
						||
| 
								 | 
							
								            conn.execute(
							 | 
						||
| 
								 | 
							
								                data_table.insert(),
							 | 
						||
| 
								 | 
							
								                data = {"key1": "value1", "key2": "value2"}
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    The :class:`_postgresql.JSONB` type includes all operations provided by
							 | 
						||
| 
								 | 
							
								    :class:`_types.JSON`, including the same behaviors for indexing
							 | 
						||
| 
								 | 
							
								    operations.
							 | 
						||
| 
								 | 
							
								    It also adds additional operators specific to JSONB, including
							 | 
						||
| 
								 | 
							
								    :meth:`.JSONB.Comparator.has_key`, :meth:`.JSONB.Comparator.has_all`,
							 | 
						||
| 
								 | 
							
								    :meth:`.JSONB.Comparator.has_any`, :meth:`.JSONB.Comparator.contains`,
							 | 
						||
| 
								 | 
							
								    and :meth:`.JSONB.Comparator.contained_by`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Like the :class:`_types.JSON` type, the :class:`_postgresql.JSONB`
							 | 
						||
| 
								 | 
							
								    type does not detect
							 | 
						||
| 
								 | 
							
								    in-place changes when used with the ORM, unless the
							 | 
						||
| 
								 | 
							
								    :mod:`sqlalchemy.ext.mutable` extension is used.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Custom serializers and deserializers
							 | 
						||
| 
								 | 
							
								    are shared with the :class:`_types.JSON` class,
							 | 
						||
| 
								 | 
							
								    using the ``json_serializer``
							 | 
						||
| 
								 | 
							
								    and ``json_deserializer`` keyword arguments.  These must be specified
							 | 
						||
| 
								 | 
							
								    at the dialect level using :func:`_sa.create_engine`.  When using
							 | 
						||
| 
								 | 
							
								    psycopg2, the serializers are associated with the jsonb type using
							 | 
						||
| 
								 | 
							
								    ``psycopg2.extras.register_default_jsonb`` on a per-connection basis,
							 | 
						||
| 
								 | 
							
								    in the same way that ``psycopg2.extras.register_default_json`` is used
							 | 
						||
| 
								 | 
							
								    to register these handlers with the json type.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    .. versionadded:: 0.9.7
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    .. seealso::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :class:`_types.JSON`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    __visit_name__ = "JSONB"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    class Comparator(JSON.Comparator):
							 | 
						||
| 
								 | 
							
								        """Define comparison operations for :class:`_types.JSON`."""
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        def has_key(self, other):
							 | 
						||
| 
								 | 
							
								            """Boolean expression.  Test for presence of a key.  Note that the
							 | 
						||
| 
								 | 
							
								            key may be a SQLA expression.
							 | 
						||
| 
								 | 
							
								            """
							 | 
						||
| 
								 | 
							
								            return self.operate(HAS_KEY, other, result_type=sqltypes.Boolean)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        def has_all(self, other):
							 | 
						||
| 
								 | 
							
								            """Boolean expression.  Test for presence of all keys in jsonb"""
							 | 
						||
| 
								 | 
							
								            return self.operate(HAS_ALL, other, result_type=sqltypes.Boolean)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        def has_any(self, other):
							 | 
						||
| 
								 | 
							
								            """Boolean expression.  Test for presence of any key in jsonb"""
							 | 
						||
| 
								 | 
							
								            return self.operate(HAS_ANY, other, result_type=sqltypes.Boolean)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        def contains(self, other, **kwargs):
							 | 
						||
| 
								 | 
							
								            """Boolean expression.  Test if keys (or array) are a superset
							 | 
						||
| 
								 | 
							
								            of/contained the keys of the argument jsonb expression.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            kwargs may be ignored by this operator but are required for API
							 | 
						||
| 
								 | 
							
								            conformance.
							 | 
						||
| 
								 | 
							
								            """
							 | 
						||
| 
								 | 
							
								            return self.operate(CONTAINS, other, result_type=sqltypes.Boolean)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        def contained_by(self, other):
							 | 
						||
| 
								 | 
							
								            """Boolean expression.  Test if keys are a proper subset of the
							 | 
						||
| 
								 | 
							
								            keys of the argument jsonb expression.
							 | 
						||
| 
								 | 
							
								            """
							 | 
						||
| 
								 | 
							
								            return self.operate(
							 | 
						||
| 
								 | 
							
								                CONTAINED_BY, other, result_type=sqltypes.Boolean
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    comparator_factory = Comparator
							 |