]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Implementation of CITEXT , unittest and documentation
authorJulian David Rath <julian.rath@semadox.com>
Mon, 6 Mar 2023 20:50:48 +0000 (15:50 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Wed, 15 Mar 2023 17:09:55 +0000 (13:09 -0400)
Added new PostgreSQL type :class:`_postgresql.CITEXT`. Pull request
courtesy Julian David Rath.

Fixes: #9416
Closes: #9417
Pull-request: https://github.com/sqlalchemy/sqlalchemy/pull/9417
Pull-request-sha: 23a83a342ad6d820ee5749ebccda04e54c373f7d

Change-Id: I54699b9457426c20afbdc0acaa41dc57644b0536

.pre-commit-config.yaml
README.unittests.rst
doc/build/changelog/unreleased_20/9416.rst [new file with mode: 0644]
doc/build/dialects/postgresql.rst
lib/sqlalchemy/dialects/postgresql/__init__.py
lib/sqlalchemy/dialects/postgresql/base.py
lib/sqlalchemy/dialects/postgresql/provision.py
lib/sqlalchemy/dialects/postgresql/types.py
test/dialect/postgresql/test_types.py
test/requirements.py

index c734beb041c11ec0294c7d8f5909e779e067deda..12b627005ef931adec7049107b0462dae130231e 100644 (file)
@@ -34,3 +34,4 @@ repos:
         entry: python tools/format_docs_code.py -f
         language: system
         types: [rst]
+        exclude: README.*
index 9d63d238fb4372ed9b89caa07df68ba8bed6f441..594728494295647ce794f98abda0ca78b63e5e16 100644 (file)
@@ -10,7 +10,6 @@ a single Python interpreter::
 
     tox
 
-
 Advanced Tox Options
 ====================
 
@@ -199,6 +198,16 @@ Additional steps specific to individual databases are as follows::
         test=# create extension hstore;
         CREATE EXTENSION
 
+    To include tests for CITEXT for PostgreSQL versions lower than 13,
+    create the CITEXT extension; for PostgreSQL 13 and above, this
+    extension is created automatically as part of the test suite if not
+    already present::
+
+        postgres=# \c test;
+        You are now connected to database "test" as user "postgresql".
+        test=# create extension citext;
+        CREATE EXTENSION
+
     Full-text search configuration should be set to English, else
     several tests of ``.match()`` will fail. This can be set (if it isn't so
     already) with:
diff --git a/doc/build/changelog/unreleased_20/9416.rst b/doc/build/changelog/unreleased_20/9416.rst
new file mode 100644 (file)
index 0000000..0b3cd0b
--- /dev/null
@@ -0,0 +1,6 @@
+.. change::
+    :tags: usecase, postgresql
+    :tickets: 9416
+
+    Added new PostgreSQL type :class:`_postgresql.CITEXT`. Pull request
+    courtesy Julian David Rath.
index ce6022c55921564fbd7b76374dba3d9898a23816..fce0e4610e80b225c06c742b79cba250407e17e7 100644 (file)
@@ -312,6 +312,7 @@ they originate from :mod:`sqlalchemy.types` or from the local dialect::
         BYTEA,
         CHAR,
         CIDR,
+        CITEXT,
         DATE,
         DOUBLE_PRECISION,
         ENUM,
@@ -372,6 +373,7 @@ construction arguments, are as follows:
 
 .. autoclass:: CIDR
 
+.. autoclass:: CITEXT
 
 .. autoclass:: DOMAIN
     :members: __init__, create, drop
index b68bd0502bd15820187b349a7c237ba2b5f9c1ff..c3ed7c1fc00e668365691548c9acfac8c0ff71e8 100644 (file)
@@ -72,6 +72,7 @@ from .ranges import TSTZRANGE
 from .types import BIT
 from .types import BYTEA
 from .types import CIDR
+from .types import CITEXT
 from .types import INET
 from .types import INTERVAL
 from .types import MACADDR
@@ -105,6 +106,7 @@ __all__ = (
     "REAL",
     "INET",
     "CIDR",
+    "CITEXT",
     "UUID",
     "BIT",
     "MACADDR",
index 3ba10380264a20cda62e6967387945c5f1734573..a50eb253ead7c848118c800e04511aae67b4e3d0 100644 (file)
@@ -1450,6 +1450,7 @@ from .types import _INT_TYPES  # noqa: F401
 from .types import BIT as BIT
 from .types import BYTEA as BYTEA
 from .types import CIDR as CIDR
+from .types import CITEXT as CITEXT
 from .types import INET as INET
 from .types import INTERVAL as INTERVAL
 from .types import MACADDR as MACADDR
@@ -1651,6 +1652,7 @@ ischema_names = {
     "real": REAL,
     "inet": INET,
     "cidr": CIDR,
+    "citext": CITEXT,
     "uuid": UUID,
     "bit": BIT,
     "bit varying": BIT,
@@ -1920,7 +1922,6 @@ class PGCompiler(compiler.SQLCompiler):
             return ""
 
     def for_update_clause(self, select, **kw):
-
         if select._for_update_arg.read:
             if select._for_update_arg.key_share:
                 tmp = " FOR KEY SHARE"
@@ -1932,7 +1933,6 @@ class PGCompiler(compiler.SQLCompiler):
             tmp = " FOR UPDATE"
 
         if select._for_update_arg.of:
-
             tables = util.OrderedSet()
             for c in select._for_update_arg.of:
                 tables.update(sql_util.surface_selectables_only(c))
@@ -1959,7 +1959,6 @@ class PGCompiler(compiler.SQLCompiler):
             return "SUBSTRING(%s FROM %s)" % (s, start)
 
     def _on_conflict_target(self, clause, **kw):
-
         if clause.constraint_target is not None:
             # target may be a name of an Index, UniqueConstraint or
             # ExcludeConstraint.  While there is a separate
@@ -1993,7 +1992,6 @@ class PGCompiler(compiler.SQLCompiler):
         return target_text
 
     def visit_on_conflict_do_nothing(self, on_conflict, **kw):
-
         target_text = self._on_conflict_target(on_conflict, **kw)
 
         if target_text:
@@ -2002,7 +2000,6 @@ class PGCompiler(compiler.SQLCompiler):
             return "ON CONFLICT DO NOTHING"
 
     def visit_on_conflict_do_update(self, on_conflict, **kw):
-
         clause = on_conflict
 
         target_text = self._on_conflict_target(on_conflict, **kw)
@@ -2110,7 +2107,6 @@ class PGCompiler(compiler.SQLCompiler):
 
 class PGDDLCompiler(compiler.DDLCompiler):
     def get_column_specification(self, column, **kwargs):
-
         colspec = self.preparer.format_column(column)
         impl_type = column.type.dialect_impl(self.dialect)
         if isinstance(impl_type, sqltypes.TypeDecorator):
@@ -2472,6 +2468,9 @@ class PGTypeCompiler(compiler.GenericTypeCompiler):
     def visit_CIDR(self, type_, **kw):
         return "CIDR"
 
+    def visit_CITEXT(self, type_, **kw):
+        return "CITEXT"
+
     def visit_MACADDR(self, type_, **kw):
         return "MACADDR"
 
@@ -2621,7 +2620,6 @@ class PGTypeCompiler(compiler.GenericTypeCompiler):
         return "BYTEA"
 
     def visit_ARRAY(self, type_, **kw):
-
         inner = self.process(type_.item_type, **kw)
         return re.sub(
             r"((?: COLLATE.*)?)$",
@@ -2644,7 +2642,6 @@ class PGTypeCompiler(compiler.GenericTypeCompiler):
 
 
 class PGIdentifierPreparer(compiler.IdentifierPreparer):
-
     reserved_words = RESERVED_WORDS
 
     def _unquote_identifier(self, value):
@@ -2843,7 +2840,6 @@ class PGExecutionContext(default.DefaultExecutionContext):
     def get_insert_default(self, column):
         if column.primary_key and column is column.table._autoincrement_column:
             if column.server_default and column.server_default.has_argument:
-
                 # pre-execute passive defaults on primary key columns
                 return self._execute_scalar(
                     "select %s" % column.server_default.arg, column.type
@@ -4222,7 +4218,6 @@ class PGDialect(default.DefaultDialect):
     def get_multi_indexes(
         self, connection, schema, filter_names, scope, kind, **kw
     ):
-
         table_oids = self._get_table_oids(
             connection, schema, filter_names, scope, kind, **kw
         )
index 4609701a2cf776385c93689767709a9eefbafb25..5c6b9fcea6344059d1a87d9bc57041fc569f16bf 100644 (file)
@@ -11,6 +11,7 @@ from ...testing.provision import drop_all_schema_objects_post_tables
 from ...testing.provision import drop_all_schema_objects_pre_tables
 from ...testing.provision import drop_db
 from ...testing.provision import log
+from ...testing.provision import post_configure_engine
 from ...testing.provision import prepare_for_drop_tables
 from ...testing.provision import set_default_schema_on_connection
 from ...testing.provision import temp_table_keyword_args
@@ -145,3 +146,11 @@ def _upsert(cfg, table, returning, set_lambda=None):
 
     stmt = stmt.returning(*returning)
     return stmt
+
+
+@post_configure_engine.for_db("postgresql")
+def _create_citext_extension(url, engine, follower_ident):
+    with engine.connect() as conn:
+        if conn.dialect.server_version_info >= (13,):
+            conn.execute(text("CREATE EXTENSION IF NOT EXISTS citext"))
+            conn.commit()
index a03fcaa392ef82acb2b8aa9f6eae5ee467ff5e3e..a6b82044b3055b50ed82302b6d2191818dc0c000 100644 (file)
@@ -255,3 +255,14 @@ class TSVECTOR(sqltypes.TypeEngine[str]):
     """
 
     __visit_name__ = "TSVECTOR"
+
+
+class CITEXT(sqltypes.TEXT):
+
+    """Provide the PostgreSQL CITEXT type.
+
+    .. versionadded:: 2.0.7
+
+    """
+
+    __visit_name__ = "CITEXT"
index 2b15c7d735af2c9bffc3a5bc7c3cad701a800d64..61d2a3107fb6c88e64d50462e1c24f2a0aacdac6 100644 (file)
@@ -40,6 +40,7 @@ from sqlalchemy.dialects.postgresql import aggregate_order_by
 from sqlalchemy.dialects.postgresql import array
 from sqlalchemy.dialects.postgresql import array_agg
 from sqlalchemy.dialects.postgresql import base
+from sqlalchemy.dialects.postgresql import CITEXT
 from sqlalchemy.dialects.postgresql import DATEMULTIRANGE
 from sqlalchemy.dialects.postgresql import DATERANGE
 from sqlalchemy.dialects.postgresql import DOMAIN
@@ -5748,3 +5749,30 @@ class JSONBCastSuiteTest(suite.JSONLegacyStringCastIndexTest):
     __requires__ = ("postgresql_jsonb",)
 
     datatype = JSONB
+
+
+class CITextTest(fixtures.TablesTest):
+    __requires__ = ("citext",)
+    __only_on__ = "postgresql"
+
+    @classmethod
+    def define_tables(cls, metadata):
+        Table(
+            "ci_test_table",
+            metadata,
+            Column("id", Integer, primary_key=True),
+            Column("caseignore_text", CITEXT),
+        )
+
+    def test_citext(self, connection):
+        ci_test_table = self.tables.ci_test_table
+        connection.execute(
+            ci_test_table.insert(),
+            {"caseignore_text": "Hello World"},
+        )
+
+        ret = connection.execute(
+            select(ci_test_table.c.caseignore_text == "hello world")
+        ).scalar()
+
+        assert ret is not None
index 67ecdc4059687147410b451f0f2c6e80331aeda0..b76e671e9fbf12fe60fb543cc3de63c2b81d14e1 100644 (file)
@@ -1463,6 +1463,10 @@ class DefaultRequirements(SuiteRequirements):
     def hstore(self):
         return self._has_pg_extension("hstore")
 
+    @property
+    def citext(self):
+        return self._has_pg_extension("citext")
+
     @property
     def btree_gist(self):
         return self._has_pg_extension("btree_gist")