smarter CONTAINS and REPLACE
This commit is contained in:
2
VERSION
2
VERSION
@@ -1 +1 @@
|
||||
Version 2.4.5-stable+timestamp.2013.04.02.16.14.35
|
||||
Version 2.4.5-stable+timestamp.2013.04.03.09.49.46
|
||||
|
||||
82
gluon/dal.py
82
gluon/dal.py
@@ -1255,24 +1255,15 @@ class BaseAdapter(ConnectionPool):
|
||||
return '(%s LIKE %s)' % (self.expand(first),
|
||||
self.expand('%'+second, 'string'))
|
||||
|
||||
def CONTAINS(self, first, second, case_sensitive=False):
|
||||
if isinstance(second,Expression):
|
||||
field = self.expand(first)
|
||||
expr = self.expand(second,'string')
|
||||
if first.type.startswith('list:'):
|
||||
expr = 'CONCAT("|", %s, "|")' % expr
|
||||
elif not first.type in ('string', 'text', 'json'):
|
||||
raise RuntimeError("Expression Not Supported")
|
||||
return 'INSTR(%s,%s)' % (field, expr)
|
||||
else:
|
||||
if first.type in ('string', 'text', 'json'):
|
||||
key = '%'+str(second).replace('%','%%')+'%'
|
||||
elif first.type.startswith('list:'):
|
||||
key = '%|'+str(second).replace('|','||').replace('%','%%')+'|%'
|
||||
else:
|
||||
raise RuntimeError("Expression Not Supported")
|
||||
op = case_sensitive and self.LIKE or self.ILIKE
|
||||
return op(first,key)
|
||||
def CONTAINS(self,first,second,case_sensitive=False):
|
||||
if first.type in ('string','text', 'json'):
|
||||
second = Expression(None,self.CONCAT('%',Expression(
|
||||
None,self.REPLACE(second,('%','%%'))),'%'))
|
||||
elif first.type.startswith('list:'):
|
||||
second = Expression(None,self.CONCAT('%|',Expression(None,self.REPLACE(
|
||||
Expression(None,self.REPLACE(second,('%','%%'))),('|','||'))),'|%'))
|
||||
op = case_sensitive and self.LIKE or self.ILIKE
|
||||
return op(first,second)
|
||||
|
||||
def EQ(self, first, second=None):
|
||||
if second is None:
|
||||
@@ -1310,9 +1301,24 @@ class BaseAdapter(ConnectionPool):
|
||||
return '(%s >= %s)' % (self.expand(first),
|
||||
self.expand(second,first.type))
|
||||
|
||||
def is_numerical_type(self, ftype):
|
||||
return ftype in ('integer','boolean','double','bigint') or \
|
||||
ftype.startswith('decimal')
|
||||
|
||||
def REPLACE(self, first, (second, third)):
|
||||
return 'REPLACE(%s,%s,%s)' % (self.expand(first,'string'),
|
||||
self.expand(second,'string'),
|
||||
self.expand(third,'string'))
|
||||
|
||||
def CONCAT(self, *items):
|
||||
return '(%s)' % ' || '.join(self.expand(x,'string') for x in items)
|
||||
|
||||
def ADD(self, first, second):
|
||||
return '(%s + %s)' % (self.expand(first),
|
||||
self.expand(second, first.type))
|
||||
if self.is_numerical_type(first.type):
|
||||
return '(%s + %s)' % (self.expand(first),
|
||||
self.expand(second, first.type))
|
||||
else:
|
||||
return self.CONCAT(first, second)
|
||||
|
||||
def SUB(self, first, second):
|
||||
return '(%s - %s)' % (self.expand(first),
|
||||
@@ -2673,22 +2679,6 @@ class PostgreSQLAdapter(BaseAdapter):
|
||||
return '(%s ILIKE %s)' % (self.expand(first),
|
||||
self.expand('%'+second,'string'))
|
||||
|
||||
def CONTAINS(self,first,second,case_sensitive=False):
|
||||
if isinstance(second,Expression):
|
||||
expr = self.expand(second,'string')
|
||||
if first.type.startswith('list:'):
|
||||
second = Expression(None,"'%%|' || %s || '|%%'" % expr)
|
||||
elif not first.type in ('string', 'text', 'json'):
|
||||
raise RuntimeError("Expression Not Supported")
|
||||
else:
|
||||
if first.type in ('string','text', 'json'):
|
||||
second = '%'+str(second).replace('%','%%')+'%'
|
||||
elif first.type.startswith('list:'):
|
||||
second = '%|'+str(second).replace('|','||')\
|
||||
.replace('%','%%')+'|%'
|
||||
op = case_sensitive and self.LIKE or self.ILIKE
|
||||
return op(first,second)
|
||||
|
||||
# GIS functions
|
||||
|
||||
def ST_ASGEOJSON(self, first, second):
|
||||
@@ -3215,6 +3205,9 @@ class MSSQLAdapter(BaseAdapter):
|
||||
def EPOCH(self, first):
|
||||
return "DATEDIFF(second, '1970-01-01 00:00:00', %s)" % self.expand(first)
|
||||
|
||||
def CONCAT(self, *items):
|
||||
return '(%s)' % ' + '.join(self.expand(x,'string') for x in items)
|
||||
|
||||
# GIS Spatial Extensions
|
||||
|
||||
# No STAsGeoJSON in MSSQL
|
||||
@@ -3473,18 +3466,13 @@ class FireBirdAdapter(BaseAdapter):
|
||||
def LENGTH(self, first):
|
||||
return "CHAR_LENGTH(%s)" % self.expand(first)
|
||||
|
||||
def CONTAINING(self,first,second):
|
||||
"case in-sensitive like operator"
|
||||
def CONTAINS(self,first,second,case_sensitive=False):
|
||||
if first.type.startswith('list:'):
|
||||
second = Expression(None,self.CONCAT('|',Expression(
|
||||
None,self.REPLACE(second,('|','||'))),'|'))
|
||||
return '(%s CONTAINING %s)' % (self.expand(first),
|
||||
self.expand(second, 'string'))
|
||||
|
||||
def CONTAINS(self, first, second, case_sensitive=False):
|
||||
if first.type in ('string','text'):
|
||||
second = str(second).replace('%','%%')
|
||||
elif first.type.startswith('list:'):
|
||||
second = '|'+str(second).replace('|','||').replace('%','%%')+'|'
|
||||
return self.CONTAINING(first,second)
|
||||
|
||||
def _drop(self,table,mode):
|
||||
sequence_name = table._sequence_name
|
||||
return ['DROP TABLE %s %s;' % (table, mode), 'DROP GENERATOR %s;' % sequence_name]
|
||||
@@ -8819,6 +8807,10 @@ class Expression(object):
|
||||
db = self.db
|
||||
return Expression(db, db._adapter.UPPER, self, None, self.type)
|
||||
|
||||
def replace(self,a,b):
|
||||
db = self.db
|
||||
return Expression(db, db._adapter.REPLACE, self, (a,b), self.type)
|
||||
|
||||
def year(self):
|
||||
db = self.db
|
||||
return Expression(db, db._adapter.EXTRACT, self, 'year', 'integer')
|
||||
|
||||
@@ -250,26 +250,26 @@ class TestSelect(unittest.TestCase):
|
||||
self.assertEqual(db.tt.insert(aa='1'), 1)
|
||||
self.assertEqual(db.tt.insert(aa='2'), 2)
|
||||
self.assertEqual(db.tt.insert(aa='3'), 3)
|
||||
self.assertEqual(len(db(db.tt.id > 0).select()), 3)
|
||||
self.assertEqual(db(db.tt.id > 0).count(), 3)
|
||||
self.assertEqual(db(db.tt.id > 0).select(orderby=~db.tt.aa
|
||||
| db.tt.id)[0].aa, '3')
|
||||
self.assertEqual(len(db(db.tt.id > 0).select(limitby=(1, 2))), 1)
|
||||
self.assertEqual(db(db.tt.id > 0).select(limitby=(1, 2))[0].aa,
|
||||
'2')
|
||||
self.assertEqual(len(db().select(db.tt.ALL)), 3)
|
||||
self.assertEqual(len(db(db.tt.aa == None).select()), 0)
|
||||
self.assertEqual(len(db(db.tt.aa != None).select()), 3)
|
||||
self.assertEqual(len(db(db.tt.aa > '1').select()), 2)
|
||||
self.assertEqual(len(db(db.tt.aa >= '1').select()), 3)
|
||||
self.assertEqual(len(db(db.tt.aa == '1').select()), 1)
|
||||
self.assertEqual(len(db(db.tt.aa != '1').select()), 2)
|
||||
self.assertEqual(len(db(db.tt.aa < '3').select()), 2)
|
||||
self.assertEqual(len(db(db.tt.aa <= '3').select()), 3)
|
||||
self.assertEqual(len(db(db.tt.aa > '1')(db.tt.aa < '3').select()), 1)
|
||||
self.assertEqual(len(db((db.tt.aa > '1') & (db.tt.aa < '3')).select()), 1)
|
||||
self.assertEqual(len(db((db.tt.aa > '1') | (db.tt.aa < '3')).select()), 3)
|
||||
self.assertEqual(len(db((db.tt.aa > '1') & ~(db.tt.aa > '2')).select()), 1)
|
||||
self.assertEqual(len(db(~(db.tt.aa > '1') & (db.tt.aa > '2')).select()), 0)
|
||||
self.assertEqual(db(db.tt.aa == None).count(), 0)
|
||||
self.assertEqual(db(db.tt.aa != None).count(), 3)
|
||||
self.assertEqual(db(db.tt.aa > '1').count(), 2)
|
||||
self.assertEqual(db(db.tt.aa >= '1').count(), 3)
|
||||
self.assertEqual(db(db.tt.aa == '1').count(), 1)
|
||||
self.assertEqual(db(db.tt.aa != '1').count(), 2)
|
||||
self.assertEqual(db(db.tt.aa < '3').count(), 2)
|
||||
self.assertEqual(db(db.tt.aa <= '3').count(), 3)
|
||||
self.assertEqual(db(db.tt.aa > '1')(db.tt.aa < '3').count(), 1)
|
||||
self.assertEqual(db((db.tt.aa > '1') & (db.tt.aa < '3')).count(), 1)
|
||||
self.assertEqual(db((db.tt.aa > '1') | (db.tt.aa < '3')).count(), 3)
|
||||
self.assertEqual(db((db.tt.aa > '1') & ~(db.tt.aa > '2')).count(), 1)
|
||||
self.assertEqual(db(~(db.tt.aa > '1') & (db.tt.aa > '2')).count(), 0)
|
||||
db.tt.drop()
|
||||
|
||||
|
||||
@@ -281,15 +281,15 @@ class TestBelongs(unittest.TestCase):
|
||||
self.assertEqual(db.tt.insert(aa='1'), 1)
|
||||
self.assertEqual(db.tt.insert(aa='2'), 2)
|
||||
self.assertEqual(db.tt.insert(aa='3'), 3)
|
||||
self.assertEqual(len(db(db.tt.aa.belongs(('1', '3'))).select()),
|
||||
self.assertEqual(db(db.tt.aa.belongs(('1', '3'))).count(),
|
||||
2)
|
||||
self.assertEqual(len(db(db.tt.aa.belongs(db(db.tt.id
|
||||
> 2)._select(db.tt.aa))).select()), 1)
|
||||
self.assertEqual(len(db(db.tt.aa.belongs(db(db.tt.aa.belongs(('1',
|
||||
'3')))._select(db.tt.aa))).select()), 2)
|
||||
self.assertEqual(len(db(db.tt.aa.belongs(db(db.tt.aa.belongs(db
|
||||
self.assertEqual(db(db.tt.aa.belongs(db(db.tt.id
|
||||
> 2)._select(db.tt.aa))).count(), 1)
|
||||
self.assertEqual(db(db.tt.aa.belongs(db(db.tt.aa.belongs(('1',
|
||||
'3')))._select(db.tt.aa))).count(), 2)
|
||||
self.assertEqual(db(db.tt.aa.belongs(db(db.tt.aa.belongs(db
|
||||
(db.tt.aa.belongs(('1', '3')))._select(db.tt.aa)))._select(
|
||||
db.tt.aa))).select()),
|
||||
db.tt.aa))).count(),
|
||||
2)
|
||||
db.tt.drop()
|
||||
|
||||
@@ -297,16 +297,17 @@ class TestBelongs(unittest.TestCase):
|
||||
class TestContains(unittest.TestCase):
|
||||
def testRun(self):
|
||||
db = DAL(DEFAULT_URI, check_reserved=['all'])
|
||||
db.define_table('tt', Field('aa', 'list:string'))
|
||||
self.assertEqual(db.tt.insert(aa=['aaa','bbb']), 1)
|
||||
self.assertEqual(db.tt.insert(aa=['bbb','ddd']), 2)
|
||||
self.assertEqual(db.tt.insert(aa=['eee','aaa']), 3)
|
||||
self.assertEqual(len(db(db.tt.aa.contains('aaa')).select()),
|
||||
2)
|
||||
self.assertEqual(len(db(db.tt.aa.contains('bbb')).select()),
|
||||
2)
|
||||
self.assertEqual(len(db(db.tt.aa.contains('aa')).select()),
|
||||
0)
|
||||
db.define_table('tt', Field('aa', 'list:string'), Field('bb','string'))
|
||||
self.assertEqual(db.tt.insert(aa=['aaa','bbb'],bb='aaa'), 1)
|
||||
self.assertEqual(db.tt.insert(aa=['bbb','ddd'],bb='abb'), 2)
|
||||
self.assertEqual(db.tt.insert(aa=['eee','aaa'],bb='acc'), 3)
|
||||
self.assertEqual(db(db.tt.aa.contains('aaa')).count(), 2)
|
||||
self.assertEqual(db(db.tt.aa.contains('bbb')).count(), 2)
|
||||
self.assertEqual(db(db.tt.aa.contains('aa')).count(), 0)
|
||||
self.assertEqual(db(db.tt.bb.contains('a')).count(), 3)
|
||||
self.assertEqual(db(db.tt.bb.contains('b')).count(), 1)
|
||||
self.assertEqual(db(db.tt.bb.contains('d')).count(), 0)
|
||||
self.assertEqual(db(db.tt.aa.contains(db.tt.bb)).count(), 1)
|
||||
db.tt.drop()
|
||||
|
||||
|
||||
@@ -316,23 +317,23 @@ class TestLike(unittest.TestCase):
|
||||
db = DAL(DEFAULT_URI, check_reserved=['all'])
|
||||
db.define_table('tt', Field('aa'))
|
||||
self.assertEqual(db.tt.insert(aa='abc'), 1)
|
||||
self.assertEqual(len(db(db.tt.aa.like('a%')).select()), 1)
|
||||
self.assertEqual(len(db(db.tt.aa.like('%b%')).select()), 1)
|
||||
self.assertEqual(len(db(db.tt.aa.like('%c')).select()), 1)
|
||||
self.assertEqual(len(db(db.tt.aa.like('%d%')).select()), 0)
|
||||
self.assertEqual(len(db(db.tt.aa.lower().like('A%')).select()), 1)
|
||||
self.assertEqual(len(db(db.tt.aa.lower().like('%B%')).select()),
|
||||
self.assertEqual(db(db.tt.aa.like('a%')).count(), 1)
|
||||
self.assertEqual(db(db.tt.aa.like('%b%')).count(), 1)
|
||||
self.assertEqual(db(db.tt.aa.like('%c')).count(), 1)
|
||||
self.assertEqual(db(db.tt.aa.like('%d%')).count(), 0)
|
||||
self.assertEqual(db(db.tt.aa.lower().like('A%')).count(), 1)
|
||||
self.assertEqual(db(db.tt.aa.lower().like('%B%')).count(),
|
||||
1)
|
||||
self.assertEqual(len(db(db.tt.aa.lower().like('%C')).select()), 1)
|
||||
self.assertEqual(len(db(db.tt.aa.upper().like('A%')).select()), 1)
|
||||
self.assertEqual(len(db(db.tt.aa.upper().like('%B%')).select()),
|
||||
self.assertEqual(db(db.tt.aa.lower().like('%C')).count(), 1)
|
||||
self.assertEqual(db(db.tt.aa.upper().like('A%')).count(), 1)
|
||||
self.assertEqual(db(db.tt.aa.upper().like('%B%')).count(),
|
||||
1)
|
||||
self.assertEqual(len(db(db.tt.aa.upper().like('%C')).select()), 1)
|
||||
self.assertEqual(db(db.tt.aa.upper().like('%C')).count(), 1)
|
||||
db.tt.drop()
|
||||
db.define_table('tt', Field('aa', 'integer'))
|
||||
self.assertEqual(db.tt.insert(aa=1111111111), 1)
|
||||
self.assertEqual(len(db(db.tt.aa.like('1%')).select()), 1)
|
||||
self.assertEqual(len(db(db.tt.aa.like('2%')).select()), 0)
|
||||
self.assertEqual(db(db.tt.aa.like('1%')).count(), 1)
|
||||
self.assertEqual(db(db.tt.aa.like('2%')).count(), 0)
|
||||
db.tt.drop()
|
||||
|
||||
|
||||
@@ -347,8 +348,8 @@ class TestDatetime(unittest.TestCase):
|
||||
10, 30)), 2)
|
||||
self.assertEqual(db.tt.insert(aa=datetime.datetime(1970, 12, 21,
|
||||
9, 30)), 3)
|
||||
self.assertEqual(len(db(db.tt.aa == datetime.datetime(1971, 12,
|
||||
21, 11, 30)).select()), 1)
|
||||
self.assertEqual(db(db.tt.aa == datetime.datetime(1971, 12,
|
||||
21, 11, 30)).count(), 1)
|
||||
self.assertEqual(db(db.tt.aa.year() == 1971).count(), 2)
|
||||
self.assertEqual(db(db.tt.aa.month() == 12).count(), 2)
|
||||
self.assertEqual(db(db.tt.aa.day() == 21).count(), 3)
|
||||
@@ -596,14 +597,14 @@ class TestCommonFilters(unittest.TestCase):
|
||||
db.t2.insert(aa='6', b=i2)
|
||||
db.t1._common_filter = lambda q: db.t1.aa>1
|
||||
self.assertEqual(db(db.t1).count(),2)
|
||||
self.assertEqual(len(db(db.t1).select()),2)
|
||||
self.assertEqual(db(db.t1).count(),2)
|
||||
q = db.t2.b==db.t1.id
|
||||
self.assertEqual(db(q).count(),2)
|
||||
self.assertEqual(len(db(q).select()),2)
|
||||
self.assertEqual(db(q).count(),2)
|
||||
self.assertEqual(len(db(db.t1).select(left=db.t2.on(q))),3)
|
||||
db.t2._common_filter = lambda q: db.t2.aa<6
|
||||
self.assertEqual(db(q).count(),1)
|
||||
self.assertEqual(len(db(q).select()),1)
|
||||
self.assertEqual(db(q).count(),1)
|
||||
self.assertEqual(len(db(db.t1).select(left=db.t2.on(q))),2)
|
||||
db.t2.drop()
|
||||
db.t1.drop()
|
||||
|
||||
Reference in New Issue
Block a user