Merge branch 'main' into avm99963-monorail

Merged commit 34d8229ae2b51fb1a15bd208e6fe6185c94f6266

GitOrigin-RevId: 7ee0917f93a577e475f8e09526dd144d245593f4
diff --git a/services/config_svc.py b/services/config_svc.py
index 27c1d3a..8ad829d 100644
--- a/services/config_svc.py
+++ b/services/config_svc.py
@@ -1,7 +1,6 @@
-# Copyright 2016 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style
-# license that can be found in the LICENSE file or at
-# https://developers.google.com/open-source/licenses/bsd
+# Copyright 2016 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 
 """Classes and functions for persistence of issue tracker configuration.
 
@@ -24,7 +23,7 @@
 from framework import exceptions
 from framework import framework_constants
 from framework import sql
-from proto import tracker_pb2
+from mrproto import tracker_pb2
 from services import caches
 from services import project_svc
 from tracker import tracker_bizobj
@@ -126,7 +125,7 @@
       label_rows_dict.update(self._DeserializeLabelRows(label_def_rows))
 
     for rows_in_shard in label_rows_dict.values():
-      rows_in_shard.sort(key=lambda row: (row[2], row[3]), reverse=True)
+      rows_in_shard.sort(key=lambda row: (row[2] or 0, row[3]), reverse=True)
 
     return label_rows_dict
 
@@ -505,7 +504,7 @@
       result.extend(pids_to_label_rows_shard[key])
     # Sort in python to reduce DB load and integrate results from shards.
     # row[2] is rank, row[3] is label name.
-    result.sort(key=lambda row: (row[2], row[3]), reverse=True)
+    result.sort(key=lambda row: (row[2] or 0, row[3]), reverse=True)
     return result
 
   def GetLabelDefRowsAnyProject(self, cnxn, where=None):
@@ -557,7 +556,8 @@
         project_id)
     return label_id_to_name.get(label_id)
 
-  def LookupLabelID(self, cnxn, project_id, label, autocreate=True):
+  def LookupLabelID(
+      self, cnxn, project_id, label, autocreate=True, case_sensitive=False):
     """Look up a label ID, optionally interning it.
 
     Args:
@@ -565,6 +565,7 @@
       project_id: int ID of the project where the statuses are defined.
       label: label string.
       autocreate: if not already in the DB, store it and generate a new ID.
+      case_sensitive: if label lookup is case sensivite
 
     Returns:
       The label ID for the given label string.
@@ -572,14 +573,19 @@
     self._EnsureLabelCacheEntry(cnxn, project_id)
     _label_id_to_name, label_name_to_id = self.label_cache.GetItem(
         project_id)
-    if label.lower() in label_name_to_id:
-      return label_name_to_id[label.lower()]
+
+    label_lower = label.lower() if not case_sensitive else label
+    if label_lower in label_name_to_id:
+      return label_name_to_id[label_lower]
+
+    if not case_sensitive:
+      where = [('LOWER(label) = %s', [label_lower])]
+    else:
+      where = [('label = %s', [label])]
 
     # Double check that the label does not already exist in the DB.
     rows = self.labeldef_tbl.Select(
-        cnxn, cols=['id'], project_id=project_id,
-        where=[('LOWER(label) = %s', [label.lower()])],
-        limit=1)
+        cnxn, cols=['id'], project_id=project_id, where=where, limit=1)
     logging.info('Double checking for %r gave %r', label, rows)
     if rows:
       self.label_row_2lc.cache.LocalInvalidate(project_id)