]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
remove case_sensitive create_engine parameter
authorMike Bayer <mike_mp@zzzcomputing.com>
Sun, 31 Oct 2021 16:54:23 +0000 (12:54 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Mon, 1 Nov 2021 19:06:25 +0000 (15:06 -0400)
Removed the previously deprecated ``case_sensitive`` parameter from
:func:`_sa.create_engine`, which would impact only the lookup of string
column names in Core-only result set rows; it had no effect on the behavior
of the ORM. The effective behavior of what ``case_sensitive`` refers
towards remains at its default value of ``True``, meaning that string names
looked up in ``row._mapping`` will match case-sensitively, just like any
other Python mapping.

Change-Id: I0dc4be3fac37d30202b1603db26fa10a110b618d

doc/build/changelog/unreleased_20/casesens.rst [new file with mode: 0644]
lib/sqlalchemy/engine/create.py
lib/sqlalchemy/engine/cursor.py
lib/sqlalchemy/engine/default.py
test/sql/test_deprecations.py

diff --git a/doc/build/changelog/unreleased_20/casesens.rst b/doc/build/changelog/unreleased_20/casesens.rst
new file mode 100644 (file)
index 0000000..fa82161
--- /dev/null
@@ -0,0 +1,18 @@
+.. change::
+    :tags: engine, removed
+
+    Removed the previously deprecated ``case_sensitive`` parameter from
+    :func:`_sa.create_engine`, which would impact only the lookup of string
+    column names in Core-only result set rows; it had no effect on the behavior
+    of the ORM. The effective behavior of what ``case_sensitive`` refers
+    towards remains at its default value of ``True``, meaning that string names
+    looked up in ``row._mapping`` will match case-sensitively, just like any
+    other Python mapping.
+
+    Note that the ``case_sensitive`` parameter was not in any way related to
+    the general subject of case sensitivity control, quoting, and "name
+    normalization" (i.e. converting for databases that consider all uppercase
+    words to be case insensitive) for DDL identifier names, which remains a
+    normal core feature of SQLAlchemy.
+
+
index 5e56ecdd9f8ff29fc9e2b9827226984de0d4e45a..b9e111647ce395d33b4541f72c2a785669cc1ce0 100644 (file)
@@ -34,13 +34,6 @@ from ..sql import compiler
         'expressions, or an "empty set" SELECT, at statement execution'
         "time.",
     ),
-    case_sensitive=(
-        "1.4",
-        "The :paramref:`_sa.create_engine.case_sensitive` parameter "
-        "is deprecated and will be removed in a future release. "
-        "Applications should work with result column names in a case "
-        "sensitive fashion.",
-    ),
 )
 def create_engine(url, **kwargs):
     """Create a new :class:`_engine.Engine` instance.
@@ -97,10 +90,6 @@ def create_engine(url, **kwargs):
 
         :ref:`connections_toplevel`
 
-    :param case_sensitive: if False, result column names
-       will match in a case-insensitive fashion, that is,
-       ``row['SomeColumn']``.
-
     :param connect_args: a dictionary of options which will be
         passed directly to the DBAPI's ``connect()`` method as
         additional keyword arguments.  See the example
index 049422617d680d90ded7b7897da4dc703661d713..071f95cff321da9ea348e8d7af37ae88e3bbe641 100644 (file)
@@ -47,7 +47,6 @@ class CursorResultMetaData(ResultMetaData):
 
     __slots__ = (
         "_keymap",
-        "case_sensitive",
         "_processors",
         "_keys",
         "_keymap_by_result_column_idx",
@@ -82,7 +81,6 @@ class CursorResultMetaData(ResultMetaData):
         tup = tuplegetter(*indexes)
 
         new_metadata = self.__class__.__new__(self.__class__)
-        new_metadata.case_sensitive = self.case_sensitive
         new_metadata._unpickled = self._unpickled
         new_metadata._processors = self._processors
         new_metadata._keys = new_keys
@@ -144,7 +142,6 @@ class CursorResultMetaData(ResultMetaData):
             else:
                 md._keymap[new] = rec
 
-        md.case_sensitive = self.case_sensitive
         md._unpickled = self._unpickled
         md._processors = self._processors
         assert not self._tuplefilter
@@ -157,10 +154,8 @@ class CursorResultMetaData(ResultMetaData):
 
     def __init__(self, parent, cursor_description):
         context = parent.context
-        dialect = context.dialect
         self._tuplefilter = None
         self._translated_indexes = None
-        self.case_sensitive = dialect.case_sensitive
         self._safe_for_cache = self._unpickled = False
 
         if context.result_column_struct:
@@ -231,10 +226,6 @@ class CursorResultMetaData(ResultMetaData):
                     for key in (metadata_entry[MD_RENDERED_NAME],) + (
                         metadata_entry[MD_OBJECTS] or ()
                     ):
-                        if not self.case_sensitive and isinstance(
-                            key, util.string_types
-                        ):
-                            key = key.lower()
                         idx = metadata_entry[MD_INDEX]
                         # if this key has been associated with more than one
                         # positional index, it's a dupe
@@ -351,8 +342,6 @@ class CursorResultMetaData(ResultMetaData):
 
         """
 
-        case_sensitive = context.dialect.case_sensitive
-
         if (
             num_ctx_cols
             and cols_are_ordered
@@ -371,9 +360,7 @@ class CursorResultMetaData(ResultMetaData):
                     idx,
                     idx,
                     rmap_entry[RM_OBJECTS],
-                    rmap_entry[RM_NAME].lower()
-                    if not case_sensitive
-                    else rmap_entry[RM_NAME],
+                    rmap_entry[RM_NAME],
                     rmap_entry[RM_RENDERED_NAME],
                     context.get_result_processor(
                         rmap_entry[RM_TYPE],
@@ -447,7 +434,6 @@ class CursorResultMetaData(ResultMetaData):
         """
 
         dialect = context.dialect
-        case_sensitive = dialect.case_sensitive
         translate_colname = context._translate_colname
         description_decoder = (
             dialect._description_decoder
@@ -475,8 +461,6 @@ class CursorResultMetaData(ResultMetaData):
                 colname = normalize_name(colname)
 
             self._keys.append(colname)
-            if not case_sensitive:
-                colname = colname.lower()
 
             yield idx, colname, untranslated, coltype
 
@@ -522,10 +506,8 @@ class CursorResultMetaData(ResultMetaData):
         result_columns,
         loose_column_name_matching,
     ):
-        dialect = context.dialect
-        case_sensitive = dialect.case_sensitive
         match_map = self._create_description_match_map(
-            result_columns, case_sensitive, loose_column_name_matching
+            result_columns, loose_column_name_matching
         )
         for (
             idx,
@@ -557,7 +539,6 @@ class CursorResultMetaData(ResultMetaData):
     def _create_description_match_map(
         cls,
         result_columns,
-        case_sensitive=True,
         loose_column_name_matching=False,
     ):
         """when matching cursor.description to a set of names that are present
@@ -569,8 +550,6 @@ class CursorResultMetaData(ResultMetaData):
         for ridx, elem in enumerate(result_columns):
             key = elem[RM_RENDERED_NAME]
 
-            if not case_sensitive:
-                key = key.lower()
             if key in d:
                 # conflicting keyname - just add the column-linked objects
                 # to the existing record.  if there is a duplicate column
@@ -614,16 +593,6 @@ class CursorResultMetaData(ResultMetaData):
 
     def _key_fallback(self, key, err, raiseerr=True):
 
-        # we apparently have not marked .case_sensitive as
-        # RemovedIn20.  I still think we should remove it as I can't
-        # imagine anyone is using it, however lets make that a separate
-        # commit.
-        if not self.case_sensitive and isinstance(key, util.string_types):
-            map_ = self._keymap
-            result = map_.get(key.lower())
-            if result is not None:
-                return result
-
         if raiseerr:
             if self._unpickled and isinstance(key, elements.ColumnElement):
                 util.raise_(
@@ -703,7 +672,6 @@ class CursorResultMetaData(ResultMetaData):
                 if isinstance(key, util.string_types + util.int_types)
             },
             "_keys": self._keys,
-            "case_sensitive": self.case_sensitive,
             "_translated_indexes": self._translated_indexes,
             "_tuplefilter": self._tuplefilter,
         }
@@ -716,7 +684,6 @@ class CursorResultMetaData(ResultMetaData):
             rec[MD_RESULT_MAP_INDEX]: rec for rec in self._keymap.values()
         }
         self._keys = state["_keys"]
-        self.case_sensitive = state["case_sensitive"]
         self._unpickled = True
         if state["_translated_indexes"]:
             self._translated_indexes = state["_translated_indexes"]
index 5046d4035d32be3d67128fa8f5ef01a99b7b64af..373c908042a2cd409111ce071cdbee57643bf4dd 100644 (file)
@@ -249,13 +249,6 @@ class DefaultDialect(interfaces.Dialect):
             'expressions, or an "empty set" SELECT, at statement execution'
             "time.",
         ),
-        case_sensitive=(
-            "1.4",
-            "The :paramref:`_sa.create_engine.case_sensitive` parameter "
-            "is deprecated and will be removed in a future release. "
-            "Applications should work with result column names in a case "
-            "sensitive fashion.",
-        ),
         server_side_cursors=(
             "1.4",
             "The :paramref:`_sa.create_engine.server_side_cursors` parameter "
@@ -272,7 +265,6 @@ class DefaultDialect(interfaces.Dialect):
         paramstyle=None,
         dbapi=None,
         implicit_returning=None,
-        case_sensitive=True,
         supports_native_boolean=None,
         max_identifier_length=None,
         label_length=None,
@@ -315,7 +307,6 @@ class DefaultDialect(interfaces.Dialect):
         self.type_compiler = self.type_compiler(self)
         if supports_native_boolean is not None:
             self.supports_native_boolean = supports_native_boolean
-        self.case_sensitive = case_sensitive
 
         self._user_defined_max_identifier_length = max_identifier_length
         if self._user_defined_max_identifier_length:
index c70e474fe1a25771722db278145965ab756bb20a..fb3c1165ac416b129fb8cd84f5314f7011c41601 100644 (file)
@@ -15,7 +15,6 @@ from sqlalchemy import exists
 from sqlalchemy import ForeignKey
 from sqlalchemy import func
 from sqlalchemy import inspect
-from sqlalchemy import INT
 from sqlalchemy import Integer
 from sqlalchemy import join
 from sqlalchemy import literal_column
@@ -31,7 +30,6 @@ from sqlalchemy import table
 from sqlalchemy import testing
 from sqlalchemy import text
 from sqlalchemy import util
-from sqlalchemy import VARCHAR
 from sqlalchemy.engine import default
 from sqlalchemy.sql import coercions
 from sqlalchemy.sql import LABEL_STYLE_TABLENAME_PLUS_COL
@@ -46,10 +44,8 @@ from sqlalchemy.sql.selectable import SelectStatementGrouping
 from sqlalchemy.testing import assert_raises_message
 from sqlalchemy.testing import assertions
 from sqlalchemy.testing import AssertsCompiledSQL
-from sqlalchemy.testing import engines
 from sqlalchemy.testing import eq_
 from sqlalchemy.testing import fixtures
-from sqlalchemy.testing import in_
 from sqlalchemy.testing import is_
 from sqlalchemy.testing import is_false
 from sqlalchemy.testing import is_true
@@ -1276,135 +1272,6 @@ class PKIncrementTest(fixtures.TablesTest):
                 self._test_autoincrement(conn)
 
 
-class CursorResultTest(fixtures.TablesTest):
-    __backend__ = True
-
-    @classmethod
-    def define_tables(cls, metadata):
-        Table(
-            "users",
-            metadata,
-            Column(
-                "user_id", INT, primary_key=True, test_needs_autoincrement=True
-            ),
-            Column("user_name", VARCHAR(20)),
-            test_needs_acid=True,
-        )
-        Table(
-            "addresses",
-            metadata,
-            Column(
-                "address_id",
-                Integer,
-                primary_key=True,
-                test_needs_autoincrement=True,
-            ),
-            Column("user_id", Integer, ForeignKey("users.user_id")),
-            Column("address", String(30)),
-            test_needs_acid=True,
-        )
-
-        Table(
-            "users2",
-            metadata,
-            Column("user_id", INT, primary_key=True),
-            Column("user_name", VARCHAR(20)),
-            test_needs_acid=True,
-        )
-
-    @classmethod
-    def insert_data(cls, connection):
-        users = cls.tables.users
-
-        connection.execute(
-            users.insert(),
-            [
-                dict(user_id=1, user_name="john"),
-                dict(user_id=2, user_name="jack"),
-            ],
-        )
-
-    @testing.requires.duplicate_names_in_cursor_description
-    def test_ambiguous_column_case_sensitive(self):
-        with testing.expect_deprecated(
-            "The create_engine.case_sensitive parameter is deprecated"
-        ):
-            eng = engines.testing_engine(options=dict(case_sensitive=False))
-
-        with eng.connect() as conn:
-            row = conn.execute(
-                select(
-                    literal_column("1").label("SOMECOL"),
-                    literal_column("1").label("SOMECOL"),
-                )
-            ).first()
-
-            assert_raises_message(
-                exc.InvalidRequestError,
-                "Ambiguous column name",
-                lambda: row._mapping["somecol"],
-            )
-
-    def test_row_case_insensitive(self):
-        with testing.expect_deprecated(
-            "The create_engine.case_sensitive parameter is deprecated"
-        ):
-            with engines.testing_engine(
-                options={"case_sensitive": False}
-            ).connect() as ins_conn:
-                row = ins_conn.execute(
-                    select(
-                        literal_column("1").label("case_insensitive"),
-                        literal_column("2").label("CaseSensitive"),
-                    )
-                ).first()
-
-                eq_(
-                    list(row._mapping.keys()),
-                    ["case_insensitive", "CaseSensitive"],
-                )
-
-                in_("case_insensitive", row._keymap)
-                in_("CaseSensitive", row._keymap)
-                in_("casesensitive", row._keymap)
-
-                eq_(row._mapping["case_insensitive"], 1)
-                eq_(row._mapping["CaseSensitive"], 2)
-                eq_(row._mapping["Case_insensitive"], 1)
-                eq_(row._mapping["casesensitive"], 2)
-
-    def test_row_case_insensitive_unoptimized(self):
-        with testing.expect_deprecated(
-            "The create_engine.case_sensitive parameter is deprecated"
-        ):
-            with engines.testing_engine(
-                options={"case_sensitive": False}
-            ).connect() as ins_conn:
-                row = ins_conn.execute(
-                    select(
-                        literal_column("1").label("case_insensitive"),
-                        literal_column("2").label("CaseSensitive"),
-                        text("3 AS screw_up_the_cols"),
-                    )
-                ).first()
-
-                eq_(
-                    list(row._mapping.keys()),
-                    ["case_insensitive", "CaseSensitive", "screw_up_the_cols"],
-                )
-
-                in_("case_insensitive", row._keymap)
-                in_("CaseSensitive", row._keymap)
-                in_("casesensitive", row._keymap)
-
-                eq_(row._mapping["case_insensitive"], 1)
-                eq_(row._mapping["CaseSensitive"], 2)
-                eq_(row._mapping["screw_up_the_cols"], 3)
-                eq_(row._mapping["Case_insensitive"], 1)
-                eq_(row._mapping["casesensitive"], 2)
-                eq_(row._mapping["screw_UP_the_cols"], 3)
-
-
 class DefaultTest(fixtures.TestBase):
     __backend__ = True