summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormike bayer <mike_mp@zzzcomputing.com>2021-11-01 19:07:43 +0000
committerGerrit Code Review <gerrit@ci3.zzzcomputing.com>2021-11-01 19:07:43 +0000
commita99ea884403de1e1f762e9b1eb635d7fc6ef8e6f (patch)
treec26be57fc65d85959bb7d440b1ff4e45debf3e35
parent8656050241766688fee8363b5bb950d3945756c1 (diff)
parent1f7c699962558d26eb5ce8ccbe934ced97f7d598 (diff)
downloadsqlalchemy-a99ea884403de1e1f762e9b1eb635d7fc6ef8e6f.tar.gz
Merge "remove case_sensitive create_engine parameter" into main
-rw-r--r--doc/build/changelog/unreleased_20/casesens.rst18
-rw-r--r--lib/sqlalchemy/engine/create.py11
-rw-r--r--lib/sqlalchemy/engine/cursor.py37
-rw-r--r--lib/sqlalchemy/engine/default.py9
-rw-r--r--test/sql/test_deprecations.py133
5 files changed, 20 insertions, 188 deletions
diff --git a/doc/build/changelog/unreleased_20/casesens.rst b/doc/build/changelog/unreleased_20/casesens.rst
new file mode 100644
index 000000000..fa82161cd
--- /dev/null
+++ b/doc/build/changelog/unreleased_20/casesens.rst
@@ -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.
+
+
diff --git a/lib/sqlalchemy/engine/create.py b/lib/sqlalchemy/engine/create.py
index 5e56ecdd9..b9e111647 100644
--- a/lib/sqlalchemy/engine/create.py
+++ b/lib/sqlalchemy/engine/create.py
@@ -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
diff --git a/lib/sqlalchemy/engine/cursor.py b/lib/sqlalchemy/engine/cursor.py
index 049422617..071f95cff 100644
--- a/lib/sqlalchemy/engine/cursor.py
+++ b/lib/sqlalchemy/engine/cursor.py
@@ -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"]
diff --git a/lib/sqlalchemy/engine/default.py b/lib/sqlalchemy/engine/default.py
index 5046d4035..373c90804 100644
--- a/lib/sqlalchemy/engine/default.py
+++ b/lib/sqlalchemy/engine/default.py
@@ -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:
diff --git a/test/sql/test_deprecations.py b/test/sql/test_deprecations.py
index c70e474fe..fb3c1165a 100644
--- a/test/sql/test_deprecations.py
+++ b/test/sql/test_deprecations.py
@@ -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