Ticket #8519: t8519-case-sensitive-like-r10949.diff
| File t8519-case-sensitive-like-r10949.diff, 9.2 KB (added by jomae, 4 months ago) |
|---|
-
trac/ticket/model.py
533 533 cursor.execute(""" 534 534 SELECT field FROM ticket_change 535 535 WHERE ticket=%%s AND time=%%s AND field %s 536 """ % db.like(), (self.id, ts, 537 db.like_escape('_comment') + '%')) 536 """ % db.like(ignore_case=False), 537 (self.id, ts, db.like_pattern(['_comment', db.ANY_CHARS], 538 ignore_case=False))) 538 539 fields = list(cursor) 539 540 rev = fields and max(int(field[8:]) for field, in fields) + 1 or 0 540 541 cursor.execute(""" … … 549 550 cursor.execute(""" 550 551 SELECT author FROM ticket_change 551 552 WHERE ticket=%%s AND time=%%s AND NOT field %s 552 LIMIT 1 553 """ % db.like(), (self.id, ts, db.like_escape('_') + '%')) 553 LIMIT 1""" % db.like(ignore_case=False), 554 (self.id, ts, db.like_pattern(['_', db.ANY_CHARS], 555 ignore_case=False))) 554 556 old_author = None 555 557 for old_author, in cursor: 556 558 break … … 584 586 SELECT field,author,oldvalue,newvalue 585 587 FROM ticket_change 586 588 WHERE ticket=%%s AND time=%%s AND field %s 587 """ % db.like(), (self.id, ts0, 588 db.like_escape('_comment') + '%')) 589 """ % db.like(ignore_case=False), 590 (self.id, ts0, db.like_pattern(['_comment', db.ANY_CHARS], 591 ignore_case=False))) 589 592 rows = sorted((int(field[8:]), author, old, new) 590 593 for field, author, old, new in cursor) 591 594 for rev, author, comment, ts in rows: … … 606 609 SELECT time,author,newvalue FROM ticket_change 607 610 WHERE ticket=%%s AND field='comment' 608 611 AND (oldvalue=%%s OR oldvalue %s) 609 """ % db.like(), (self.id, scnum, 610 '%' + db.like_escape('.' + scnum))) 612 """ % db.like(ignore_case=False), 613 (self.id, scnum, db.like_pattern([db.ANY_CHARS, '.' + scnum], 614 ignore_case=False))) 611 615 for row in cursor: 612 616 return row 613 617 … … 638 642 SELECT author FROM ticket_change 639 643 WHERE ticket=%%s AND time=%%s AND NOT field %s 640 644 LIMIT 1 641 """ % db.like(), (self.id, ts, db.like_escape('_') + '%')) 645 """ % db.like(ignore_case=False), 646 (self.id, ts, db.like_pattern(['_', db.ANY_CHARS], 647 ignore_case=False))) 642 648 for author, in cursor: 643 649 break 644 650 return (ts, author, comment) -
trac/versioncontrol/cache.py
325 325 sfirst = self.db_rev(first) 326 326 cursor.execute("SELECT DISTINCT rev FROM node_change " 327 327 "WHERE repos=%%s AND rev>=%%s AND rev<=%%s " 328 " AND (path=%%s OR path %s)" % db.like(), 328 " AND (path=%%s OR path %s)" % \ 329 db.like(ignore_case=False), 329 330 (self.id, sfirst, slast, path, 330 db.like_escape(path + '/') + '%')) 331 db.like_pattern([path + '/', db.ANY_CHARS], 332 ignore_case=False))) 331 333 return [int(row[0]) for row in cursor] 332 334 333 335 def has_node(self, path, rev=None): … … 362 364 if path: 363 365 path = path.lstrip('/') 364 366 # changes on path itself or its children 365 sql += " AND (path=%s OR path " + db.like() 366 args.extend((path, db.like_escape(path + '/') + '%')) 367 sql += " AND (path=%s OR path " + db.like(ignore_case=False) 368 args.extend((path, db.like_pattern([path + '/', db.ANY_CHARS], 369 ignore_case=False))) 367 370 # deletion of path ancestors 368 371 components = path.lstrip('/').split('/') 369 372 parents = ','.join(('%s',) * len(components)) -
trac/db/sqlite_backend.py
26 26 from trac.util.translation import _ 27 27 28 28 _like_escape_re = re.compile(r'([/_%])') 29 _glob_escape_re = re.compile(r'[*?[]') 29 30 30 31 try: 31 32 import pysqlite2.dbapi2 as sqlite … … 297 298 def concat(self, *args): 298 299 return '||'.join(args) 299 300 300 def like(self): 301 """Return a case-insensitive LIKE clause.""" 302 if sqlite_version >= (3, 1, 0): 303 return "LIKE %s ESCAPE '/'" 301 def like(self, ignore_case=True): 302 """Return a LIKE clause.""" 303 if ignore_case: 304 if sqlite_version >= (3, 1, 0): 305 return "LIKE %s ESCAPE '/'" 306 else: 307 return 'LIKE %s' 304 308 else: 305 return ' LIKE%s'309 return 'GLOB %s' 306 310 307 def like_escape(self, text): 308 if sqlite_version >= (3, 1, 0): 309 return _like_escape_re.sub(r'/\1', text) 311 def like_escape(self, text, ignore_case=True): 312 if ignore_case: 313 if sqlite_version >= (3, 1, 0): 314 return _like_escape_re.sub(r'/\1', text) 315 else: 316 return text 310 317 else: 311 return text318 return _glob_escape_re.sub(r'[\g<0>]', text) 312 319 320 def like_pattern(self, texts, ignore_case=True): 321 """Return a pattern for LIKE operator.""" 322 if ignore_case: 323 any_chars = '%' 324 single_char = '_' 325 else: 326 any_chars = '*' 327 single_char = '?' 328 329 pattern = [] 330 if not isinstance(texts, (list, tuple)): 331 texts = [texts] 332 for text in texts: 333 if text is self.ANY_CHARS: 334 text = any_chars 335 elif text is self.SINGLE_CHAR: 336 text = single_char 337 else: 338 text = self.like_escape(text, ignore_case=ignore_case) 339 pattern.append(text) 340 return ''.join(pattern) 341 313 342 def quote(self, identifier): 314 343 """Return the quoted identifier.""" 315 344 return "`%s`" % identifier.replace('`', '``') -
trac/db/mysql_backend.py
242 242 def concat(self, *args): 243 243 return 'concat(%s)' % ', '.join(args) 244 244 245 def like(self): 246 """Return a case-insensitive LIKE clause.""" 247 return "LIKE %s COLLATE utf8_general_ci ESCAPE '/'" 245 def like(self, ignore_case=True): 246 """Return a LIKE clause.""" 247 if ignore_case: 248 return "LIKE %s COLLATE utf8_general_ci ESCAPE '/'" 249 else: 250 return "LIKE %s ESCAPE '/'" 248 251 249 def like_escape(self, text ):252 def like_escape(self, text, ignore_case=True): 250 253 return _like_escape_re.sub(r'/\1', text) 251 254 252 255 def quote(self, identifier): -
trac/db/postgres_backend.py
234 234 def concat(self, *args): 235 235 return '||'.join(args) 236 236 237 def like(self): 238 """Return a case-insensitive LIKE clause.""" 239 return "ILIKE %s ESCAPE '/'" 237 def like(self, ignore_case=True): 238 """Return a LIKE clause.""" 239 if ignore_case: 240 return "ILIKE %s ESCAPE '/'" 241 else: 242 return "LIKE %s ESCAPE '/'" 240 243 241 def like_escape(self, text ):244 def like_escape(self, text, ignore_case=True): 242 245 return _like_escape_re.sub(r'/\1', text) 243 246 244 247 def quote(self, identifier): -
trac/db/util.py
94 94 """ 95 95 __slots__ = ('cnx', 'log') 96 96 97 ANY_CHARS = object() 98 SINGLE_CHAR = object() 99 97 100 def __init__(self, cnx, log=None): 98 101 self.cnx = cnx 99 102 self.log = log 100 103 101 104 def __getattr__(self, name): 102 105 return getattr(self.cnx, name) 106 107 def like_pattern(self, texts, ignore_case=True): 108 pattern = [] 109 if not isinstance(texts, (list, tuple)): 110 texts = [texts] 111 for text in texts: 112 if text is self.ANY_CHARS: 113 text = '%' 114 elif text is self.SINGLE_CHAR: 115 text = '_' 116 else: 117 text = self.like_escape(text, ignore_case=ignore_case) 118 pattern.append(text) 119 return ''.join(pattern)
