]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
add special rule to honor UPPERCASE name for TExtualSelect
authorMike Bayer <mike_mp@zzzcomputing.com>
Wed, 3 Jul 2024 19:46:30 +0000 (15:46 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Fri, 5 Jul 2024 15:07:59 +0000 (11:07 -0400)
Fixed issue in name normalization (e.g. "uppercase" backends like Oracle)
where using a :class:`.TextualSelect` would not properly maintain as
uppercase column names that were quoted as uppercase, even though
the :class:`.TextualSelect` includes a :class:`.Column` that explicitly
holds this uppercase name.

Fixes: #10788
Change-Id: I542a2313d22cf13db6760fe02ac659c97b5aa29e

doc/build/changelog/unreleased_21/10788.rst [new file with mode: 0644]
lib/sqlalchemy/engine/cursor.py
lib/sqlalchemy/testing/suite/test_results.py

diff --git a/doc/build/changelog/unreleased_21/10788.rst b/doc/build/changelog/unreleased_21/10788.rst
new file mode 100644 (file)
index 0000000..63f6af8
--- /dev/null
@@ -0,0 +1,9 @@
+.. change::
+    :tags: bug, sql
+    :tickets: 10788
+
+    Fixed issue in name normalization (e.g. "uppercase" backends like Oracle)
+    where using a :class:`.TextualSelect` would not properly maintain as
+    uppercase column names that were quoted as uppercase, even though
+    the :class:`.TextualSelect` includes a :class:`.Column` that explicitly
+    holds this uppercase name.
index 9ff5cdeb86e956ffc5bd3f840d05adf5ca0e0d01..8a2a47cb897c462e20de6d53b9813bbe26d79f6f 100644 (file)
@@ -676,8 +676,6 @@ class CursorResultMetaData(ResultMetaData):
             dialect.normalize_name if dialect.requires_name_normalize else None
         )
 
-        self._keys = []
-
         untranslated = None
 
         for idx, rec in enumerate(cursor_description):
@@ -697,14 +695,10 @@ class CursorResultMetaData(ResultMetaData):
                 colname = normalize_name(colname)
 
             if driver_column_names:
-                self._keys.append(unnormalized)
-
-                yield idx, colname, unnormalized, coltype
+                yield idx, colname, unnormalized, unnormalized, coltype
 
             else:
-                self._keys.append(colname)
-
-                yield idx, colname, untranslated, coltype
+                yield idx, colname, unnormalized, untranslated, coltype
 
     def _merge_textual_cols_by_position(
         self, context, cursor_description, result_columns, driver_column_names
@@ -719,9 +713,13 @@ class CursorResultMetaData(ResultMetaData):
             )
         seen = set()
 
+        self._keys = []
+
+        uses_denormalize = context.dialect.requires_name_normalize
         for (
             idx,
             colname,
+            unnormalized,
             untranslated,
             coltype,
         ) in self._colnames_from_description(
@@ -738,11 +736,43 @@ class CursorResultMetaData(ResultMetaData):
                         "in textual SQL: %r" % obj[0]
                     )
                 seen.add(obj[0])
+
+                # special check for all uppercase unnormalized name;
+                # use the unnormalized name as the key.
+                # see #10788
+                # if these names don't match, then we still honor the
+                # cursor.description name as the key and not what the
+                # Column has, see
+                # test_resultset.py::PositionalTextTest::test_via_column
+                if (
+                    uses_denormalize
+                    and unnormalized == ctx_rec[RM_RENDERED_NAME]
+                ):
+                    result_name = unnormalized
+                else:
+                    result_name = colname
             else:
                 mapped_type = sqltypes.NULLTYPE
                 obj = None
                 ridx = None
-            yield idx, ridx, colname, mapped_type, coltype, obj, untranslated
+
+                result_name = colname
+
+            if driver_column_names:
+                assert untranslated is not None
+                self._keys.append(untranslated)
+            else:
+                self._keys.append(result_name)
+
+            yield (
+                idx,
+                ridx,
+                result_name,
+                mapped_type,
+                coltype,
+                obj,
+                untranslated,
+            )
 
     def _merge_cols_by_name(
         self,
@@ -757,9 +787,12 @@ class CursorResultMetaData(ResultMetaData):
         )
         mapped_type: TypeEngine[Any]
 
+        self._keys = []
+
         for (
             idx,
             colname,
+            unnormalized,
             untranslated,
             coltype,
         ) in self._colnames_from_description(
@@ -775,6 +808,12 @@ class CursorResultMetaData(ResultMetaData):
                 obj = ctx_rec[1]
                 mapped_type = ctx_rec[2]
                 result_columns_idx = ctx_rec[3]
+
+            if driver_column_names:
+                assert untranslated is not None
+                self._keys.append(untranslated)
+            else:
+                self._keys.append(colname)
             yield (
                 idx,
                 result_columns_idx,
@@ -831,14 +870,24 @@ class CursorResultMetaData(ResultMetaData):
     def _merge_cols_by_none(
         self, context, cursor_description, driver_column_names
     ):
+        self._keys = []
+
         for (
             idx,
             colname,
+            unnormalized,
             untranslated,
             coltype,
         ) in self._colnames_from_description(
             context, cursor_description, driver_column_names
         ):
+
+            if driver_column_names:
+                assert untranslated is not None
+                self._keys.append(untranslated)
+            else:
+                self._keys.append(colname)
+
             yield (
                 idx,
                 None,
index 05e35d0ebf31a63d3f460b32a3c9b916f2933d16..639a5d056b7ce7845b3d32849817491dd919973a 100644 (file)
@@ -228,7 +228,7 @@ class NameDenormalizeTest(fixtures.TablesTest):
                     },
                 )
             else:
-                if stmt_type.core_select:
+                if stmt_type.core_select or stmt_type.text_cols:
                     self._assert_row_mapping(
                         row,
                         {
@@ -252,11 +252,7 @@ class NameDenormalizeTest(fixtures.TablesTest):
                             "all_lowercase_quoted": 8,
                             "all_uppercase_quoted": 9,
                         },
-                        include_cols=(
-                            self.tables.denormalize_table.c
-                            if stmt_type.text_cols
-                            else None
-                        ),
+                        include_cols=None,
                     )
 
         else: