From 2c6e9f8a9aeb69117d941548bd055f301ecc526e Mon Sep 17 00:00:00 2001 From: d-w-moore Date: Mon, 27 Apr 2026 01:27:10 -0400 Subject: [PATCH 1/3] [_600] prevent case-sensitive testing with BETWEEN where infeasible --- irods/test/query_test.py | 74 ++++++++++++++++++++++++++++------------ 1 file changed, 52 insertions(+), 22 deletions(-) diff --git a/irods/test/query_test.py b/irods/test/query_test.py index b04c29d8..ac6649f2 100644 --- a/irods/test/query_test.py +++ b/irods/test/query_test.py @@ -44,6 +44,9 @@ def rows_returned(query): class TestQuery(unittest.TestCase): + + _csq = None + Iterate_to_exhaust_statement_table = range(IRODS_STATEMENT_TABLE_SIZE + 1) More_than_one_batch = 2 * MAX_SQL_ROWS # may need to increase if PRC default page @@ -51,9 +54,39 @@ class TestQuery(unittest.TestCase): register_resc = "" + @property + def case_sensitive_testing_feasible(self): + """ + Return result of the probe into whether lexical order of query columns has been found compatible with + case sensitive operators such as >, <, between. (MySQL can fail in the default DB configuration, + whereas PostgreSQL does not). + """ + return self._csq in (True,) + + @staticmethod + def _query_columns_case_sensitive_by_default(ses): + home = helpers.home_collection(ses) + data_objs = [] + try: + for x in ('a', 'b', 'A', 'B'): + data_objs.append(ses.data_objects.create(f'{home}/{x}')) + lower = { r[DataObject.name] for r in ses.query(DataObject.name).filter(DataObject.name >= 'a', DataObject.name <= 'b') } + if {'A','B'} & lower: + return False + upper = { r[DataObject.name] for r in ses.query(DataObject.name).filter(DataObject.name >= 'A', DataObject.name <= 'B') } + if {'a','b'} & upper: + return False + return True + finally: + for d in data_objs: + d.unlink(force=True) + @classmethod def setUpClass(cls): with helpers.make_session() as sess: + if cls._csq is None: + cls._csq = cls._query_columns_case_sensitive_by_default(sess) + resource_name = helpers.get_register_resource(sess) if resource_name: cls.register_resc = resource_name @@ -214,28 +247,25 @@ def test_files_query_case_sensitive(self): self.assertEqual(len(result12), 0) # BETWEEN tests - - # TODO(#600): Uncomment these lines and/or make a new test when database flavor can be detected. - # The resultset for BETWEEN queries can differ from database to database. - - # result13 = self.sess.query(DataObject.name).filter( - # Collection.name == self.coll_path).filter( - # Between(DataObject.name, [self.case_sensitive_obj_name1, - # self.case_sensitive_obj_name1 + "_"])).all() - # self.assertTrue(result13.has_value(self.case_sensitive_obj_name1)) - # self.assertEqual(len(result13), 1) - - # result14 = self.sess.query(DataObject.name).filter( - # Collection.name == self.coll_path).filter( - # Between(DataObject.name, [str.lower(self.case_sensitive_obj_name1), - # str.lower(self.case_sensitive_obj_name1) + "_"])).all() - # self.assertEqual(len(result14), 0) - - # result15 = self.sess.query(DataObject.name).filter( - # Collection.name == self.coll_path).filter( - # Between(DataObject.name, [str.upper(self.case_sensitive_obj_name1), - # str.upper(self.case_sensitive_obj_name1) + "_"])).all() - # self.assertEqual(len(result15), 0) + if self.case_sensitive_testing_feasible: + result13 = self.sess.query(DataObject.name).filter( + Collection.name == self.coll_path).filter( + Between(DataObject.name, [self.case_sensitive_obj_name1, + self.case_sensitive_obj_name1 + "_"])).all() + self.assertTrue(result13.has_value(self.case_sensitive_obj_name1)) + self.assertEqual(len(result13), 1) + + result14 = self.sess.query(DataObject.name).filter( + Collection.name == self.coll_path).filter( + Between(DataObject.name, [str.lower(self.case_sensitive_obj_name1), + str.lower(self.case_sensitive_obj_name1) + "_"])).all() + self.assertEqual(len(result14), 0) + + result15 = self.sess.query(DataObject.name).filter( + Collection.name == self.coll_path).filter( + Between(DataObject.name, [str.upper(self.case_sensitive_obj_name1), + str.upper(self.case_sensitive_obj_name1) + "_"])).all() + self.assertEqual(len(result15), 0) def test_files_query_case_insensitive(self): # This tests that GenQueries are case-insensitive when the case_sensitive From 3402c7e359005dd2d1e41549da471ab6fa182553 Mon Sep 17 00:00:00 2001 From: d-w-moore Date: Wed, 29 Apr 2026 10:59:29 -0400 Subject: [PATCH 2/3] Revert "[_600] prevent case-sensitive testing with BETWEEN where infeasible" This reverts commit 2c6e9f8a9aeb69117d941548bd055f301ecc526e. --- irods/test/query_test.py | 74 ++++++++++++---------------------------- 1 file changed, 22 insertions(+), 52 deletions(-) diff --git a/irods/test/query_test.py b/irods/test/query_test.py index ac6649f2..b04c29d8 100644 --- a/irods/test/query_test.py +++ b/irods/test/query_test.py @@ -44,9 +44,6 @@ def rows_returned(query): class TestQuery(unittest.TestCase): - - _csq = None - Iterate_to_exhaust_statement_table = range(IRODS_STATEMENT_TABLE_SIZE + 1) More_than_one_batch = 2 * MAX_SQL_ROWS # may need to increase if PRC default page @@ -54,39 +51,9 @@ class TestQuery(unittest.TestCase): register_resc = "" - @property - def case_sensitive_testing_feasible(self): - """ - Return result of the probe into whether lexical order of query columns has been found compatible with - case sensitive operators such as >, <, between. (MySQL can fail in the default DB configuration, - whereas PostgreSQL does not). - """ - return self._csq in (True,) - - @staticmethod - def _query_columns_case_sensitive_by_default(ses): - home = helpers.home_collection(ses) - data_objs = [] - try: - for x in ('a', 'b', 'A', 'B'): - data_objs.append(ses.data_objects.create(f'{home}/{x}')) - lower = { r[DataObject.name] for r in ses.query(DataObject.name).filter(DataObject.name >= 'a', DataObject.name <= 'b') } - if {'A','B'} & lower: - return False - upper = { r[DataObject.name] for r in ses.query(DataObject.name).filter(DataObject.name >= 'A', DataObject.name <= 'B') } - if {'a','b'} & upper: - return False - return True - finally: - for d in data_objs: - d.unlink(force=True) - @classmethod def setUpClass(cls): with helpers.make_session() as sess: - if cls._csq is None: - cls._csq = cls._query_columns_case_sensitive_by_default(sess) - resource_name = helpers.get_register_resource(sess) if resource_name: cls.register_resc = resource_name @@ -247,25 +214,28 @@ def test_files_query_case_sensitive(self): self.assertEqual(len(result12), 0) # BETWEEN tests - if self.case_sensitive_testing_feasible: - result13 = self.sess.query(DataObject.name).filter( - Collection.name == self.coll_path).filter( - Between(DataObject.name, [self.case_sensitive_obj_name1, - self.case_sensitive_obj_name1 + "_"])).all() - self.assertTrue(result13.has_value(self.case_sensitive_obj_name1)) - self.assertEqual(len(result13), 1) - - result14 = self.sess.query(DataObject.name).filter( - Collection.name == self.coll_path).filter( - Between(DataObject.name, [str.lower(self.case_sensitive_obj_name1), - str.lower(self.case_sensitive_obj_name1) + "_"])).all() - self.assertEqual(len(result14), 0) - - result15 = self.sess.query(DataObject.name).filter( - Collection.name == self.coll_path).filter( - Between(DataObject.name, [str.upper(self.case_sensitive_obj_name1), - str.upper(self.case_sensitive_obj_name1) + "_"])).all() - self.assertEqual(len(result15), 0) + + # TODO(#600): Uncomment these lines and/or make a new test when database flavor can be detected. + # The resultset for BETWEEN queries can differ from database to database. + + # result13 = self.sess.query(DataObject.name).filter( + # Collection.name == self.coll_path).filter( + # Between(DataObject.name, [self.case_sensitive_obj_name1, + # self.case_sensitive_obj_name1 + "_"])).all() + # self.assertTrue(result13.has_value(self.case_sensitive_obj_name1)) + # self.assertEqual(len(result13), 1) + + # result14 = self.sess.query(DataObject.name).filter( + # Collection.name == self.coll_path).filter( + # Between(DataObject.name, [str.lower(self.case_sensitive_obj_name1), + # str.lower(self.case_sensitive_obj_name1) + "_"])).all() + # self.assertEqual(len(result14), 0) + + # result15 = self.sess.query(DataObject.name).filter( + # Collection.name == self.coll_path).filter( + # Between(DataObject.name, [str.upper(self.case_sensitive_obj_name1), + # str.upper(self.case_sensitive_obj_name1) + "_"])).all() + # self.assertEqual(len(result15), 0) def test_files_query_case_insensitive(self): # This tests that GenQueries are case-insensitive when the case_sensitive From 77562a0cbe09c5bba07eafa73c60735b7edfba52 Mon Sep 17 00:00:00 2001 From: d-w-moore Date: Thu, 30 Apr 2026 15:27:32 -0400 Subject: [PATCH 3/3] remove case-sensitive tests entirely. Not all databases are predictable as lexicographical order mixes up case in arbitrarily varying ways. 'A' can be less than, greater than, or equal to 'a' depending on the DB and/or its configuration. --- irods/test/query_test.py | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/irods/test/query_test.py b/irods/test/query_test.py index b04c29d8..a4f4ecdb 100644 --- a/irods/test/query_test.py +++ b/irods/test/query_test.py @@ -213,30 +213,6 @@ def test_files_query_case_sensitive(self): ) self.assertEqual(len(result12), 0) - # BETWEEN tests - - # TODO(#600): Uncomment these lines and/or make a new test when database flavor can be detected. - # The resultset for BETWEEN queries can differ from database to database. - - # result13 = self.sess.query(DataObject.name).filter( - # Collection.name == self.coll_path).filter( - # Between(DataObject.name, [self.case_sensitive_obj_name1, - # self.case_sensitive_obj_name1 + "_"])).all() - # self.assertTrue(result13.has_value(self.case_sensitive_obj_name1)) - # self.assertEqual(len(result13), 1) - - # result14 = self.sess.query(DataObject.name).filter( - # Collection.name == self.coll_path).filter( - # Between(DataObject.name, [str.lower(self.case_sensitive_obj_name1), - # str.lower(self.case_sensitive_obj_name1) + "_"])).all() - # self.assertEqual(len(result14), 0) - - # result15 = self.sess.query(DataObject.name).filter( - # Collection.name == self.coll_path).filter( - # Between(DataObject.name, [str.upper(self.case_sensitive_obj_name1), - # str.upper(self.case_sensitive_obj_name1) + "_"])).all() - # self.assertEqual(len(result15), 0) - def test_files_query_case_insensitive(self): # This tests that GenQueries are case-insensitive when the case_sensitive # option is set to false.