diff --git a/VERSION b/VERSION index 83e62b15..1ce3eb84 100644 --- a/VERSION +++ b/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 diff --git a/gluon/dal.py b/gluon/dal.py index f20aa3f1..c96f04a9 100644 --- a/gluon/dal.py +++ b/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') diff --git a/gluon/tests/test_dal.py b/gluon/tests/test_dal.py index c2cca719..9ae2a951 100644 --- a/gluon/tests/test_dal.py +++ b/gluon/tests/test_dal.py @@ -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()