db.t.f.epoch(), thanks Niphlod

This commit is contained in:
mdipierro
2012-09-14 15:51:58 -05:00
parent 47bcab6b26
commit 5f503d0427
3 changed files with 78 additions and 58 deletions
+1 -1
View File
@@ -1 +1 @@
Version 2.0.9 (2012-09-14 09:42:11) stable Version 2.0.9 (2012-09-14 15:51:53) stable
+69 -50
View File
@@ -40,7 +40,7 @@ Example of usage:
>>> # from dal import DAL, Field >>> # from dal import DAL, Field
### create DAL connection (and create DB if it doesn't exist) ### create DAL connection (and create DB if it doesn't exist)
>>> db = DAL(('sqlite://storage.sqlite','mysql://a:b@localhost/x'), >>> db = DAL(('sqlite://storage.sqlite','mysql://a:b@localhost/x'),
... folder=None) ... folder=None)
### define a table 'person' (create/alter as necessary) ### define a table 'person' (create/alter as necessary)
@@ -330,7 +330,7 @@ if not 'google' in DRIVERS:
DRIVERS.append('Sybase(Sybase)') DRIVERS.append('Sybase(Sybase)')
except ImportError: except ImportError:
logger.debug('no Sybase driver') logger.debug('no Sybase driver')
try: try:
import kinterbasdb import kinterbasdb
DRIVERS.append('Interbase(kinterbasdb)') DRIVERS.append('Interbase(kinterbasdb)')
@@ -342,7 +342,7 @@ if not 'google' in DRIVERS:
import fdb import fdb
DRIVERS.append('Firbird(fdb)') DRIVERS.append('Firbird(fdb)')
except ImportError: except ImportError:
logger.debug('no Firebird driver fdb') logger.debug('no Firebird driver fdb')
##### #####
try: try:
import firebirdsql import firebirdsql
@@ -660,7 +660,7 @@ class BaseAdapter(ConnectionPool):
def find_driver(self,adapter_args,uri=None): def find_driver(self,adapter_args,uri=None):
if hasattr(self,'driver') and self.driver!=None: if hasattr(self,'driver') and self.driver!=None:
return return
drivers_available = [driver for driver in self.drivers drivers_available = [driver for driver in self.drivers
if driver in globals()] if driver in globals()]
if uri: if uri:
@@ -668,7 +668,7 @@ class BaseAdapter(ConnectionPool):
request_driver = items[1] if len(items)>1 else None request_driver = items[1] if len(items)>1 else None
else: else:
request_driver = None request_driver = None
request_driver = request_driver or adapter_args.get('driver') request_driver = request_driver or adapter_args.get('driver')
if request_driver: if request_driver:
if request_driver in drivers_available: if request_driver in drivers_available:
self.driver_name = request_driver self.driver_name = request_driver
@@ -680,7 +680,7 @@ class BaseAdapter(ConnectionPool):
self.driver = globals().get(self.driver_name) self.driver = globals().get(self.driver_name)
else: else:
raise RuntimeError, "no driver available %s", self.drivers raise RuntimeError, "no driver available %s", self.drivers
def __init__(self, db,uri,pool_size=0, folder=None, db_codec='UTF-8', def __init__(self, db,uri,pool_size=0, folder=None, db_codec='UTF-8',
credential_decoder=IDENTITY, driver_args={}, credential_decoder=IDENTITY, driver_args={},
@@ -799,7 +799,7 @@ class BaseAdapter(ConnectionPool):
else: else:
schema = parms[0] schema = parms[0]
ftype = "SELECT AddGeometryColumn ('%%(schema)s', '%%(tablename)s', '%%(fieldname)s', %%(srid)s, '%s', %%(dimension)s);" % types[geotype] ftype = "SELECT AddGeometryColumn ('%%(schema)s', '%%(tablename)s', '%%(fieldname)s', %%(srid)s, '%s', %%(dimension)s);" % types[geotype]
ftype = ftype % dict(schema=schema, ftype = ftype % dict(schema=schema,
tablename=tablename, tablename=tablename,
fieldname=field_name, srid=srid, fieldname=field_name, srid=srid,
dimension=dimension) dimension=dimension)
@@ -890,7 +890,7 @@ class BaseAdapter(ConnectionPool):
else: else:
table._dbt = pjoin( table._dbt = pjoin(
dbpath, '%s_%s.table' % (table._db._uri_hash, tablename)) dbpath, '%s_%s.table' % (table._db._uri_hash, tablename))
if table._dbt: if table._dbt:
table._loggername = pjoin(dbpath, 'sql.log') table._loggername = pjoin(dbpath, 'sql.log')
logfile = self.file_open(table._loggername, 'a') logfile = self.file_open(table._loggername, 'a')
@@ -1002,7 +1002,7 @@ class BaseAdapter(ConnectionPool):
query = ['ALTER TABLE %s DROP %s;' % (tablename, key)] query = ['ALTER TABLE %s DROP %s;' % (tablename, key)]
metadata_change = True metadata_change = True
elif sql_fields[key]['sql'] != sql_fields_old[key]['sql'] \ elif sql_fields[key]['sql'] != sql_fields_old[key]['sql'] \
and not (key in table.fields and and not (key in table.fields and
isinstance(table[key].type, SQLCustomType)) \ isinstance(table[key].type, SQLCustomType)) \
and not sql_fields[key]['type'].startswith('reference')\ and not sql_fields[key]['type'].startswith('reference')\
and not sql_fields[key]['type'].startswith('double')\ and not sql_fields[key]['type'].startswith('double')\
@@ -1073,6 +1073,9 @@ class BaseAdapter(ConnectionPool):
def EXTRACT(self, first, what): def EXTRACT(self, first, what):
return "EXTRACT(%s FROM %s)" % (what, self.expand(first)) return "EXTRACT(%s FROM %s)" % (what, self.expand(first))
def EPOCH(self, first):
return self.EXTRACT(first, 'epoch')
def AGGREGATE(self, first, what): def AGGREGATE(self, first, what):
return "%s(%s)" % (what, self.expand(first)) return "%s(%s)" % (what, self.expand(first))
@@ -1273,7 +1276,7 @@ class BaseAdapter(ConnectionPool):
first = expression.first first = expression.first
second = expression.second second = expression.second
op = expression.op op = expression.op
if not second is None: if not second is None:
return op(first, second) return op(first, second)
elif not first is None: elif not first is None:
return op(first) return op(first)
@@ -1580,14 +1583,14 @@ class BaseAdapter(ConnectionPool):
""" """
sql = self._select(query, fields, attributes) sql = self._select(query, fields, attributes)
cache = attributes.get('cache', None) cache = attributes.get('cache', None)
if cache and attributes.get('cacheable',False): if cache and attributes.get('cacheable',False):
del attributes['cache'] del attributes['cache']
(cache_model, time_expire) = cache (cache_model, time_expire) = cache
key = self.uri + '/' + sql key = self.uri + '/' + sql
if len(key)>200: key = hashlib.md5(key).hexdigest() if len(key)>200: key = hashlib.md5(key).hexdigest()
args = (sql,fields,attributes) args = (sql,fields,attributes)
return cache_model( return cache_model(
key, key,
lambda self=self,args=args:self._select_aux(*args), lambda self=self,args=args:self._select_aux(*args),
time_expire) time_expire)
else: else:
@@ -1906,7 +1909,7 @@ class BaseAdapter(ConnectionPool):
for (i,row) in enumerate(rows): for (i,row) in enumerate(rows):
new_row = Row() new_row = Row()
for (j,colname) in enumerate(colnames): for (j,colname) in enumerate(colnames):
value = row[j] value = row[j]
tmp = tmps[j] tmp = tmps[j]
if tmp: if tmp:
(tablename,fieldname,table,field,ft) = tmp (tablename,fieldname,table,field,ft) = tmp
@@ -1920,14 +1923,14 @@ class BaseAdapter(ConnectionPool):
if field.filter_out: if field.filter_out:
value = field.filter_out(value) value = field.filter_out(value)
colset[fieldname] = value colset[fieldname] = value
# for backward compatibility # for backward compatibility
if ft=='id' and fieldname!='id' and \ if ft=='id' and fieldname!='id' and \
not 'id' in table.fields: not 'id' in table.fields:
colset['id'] = value colset['id'] = value
if ft == 'id' and not cacheable: if ft == 'id' and not cacheable:
# temporary hack to deal with # temporary hack to deal with
# GoogleDatastoreAdapter # GoogleDatastoreAdapter
# references # references
if isinstance(self, GoogleDatastoreAdapter): if isinstance(self, GoogleDatastoreAdapter):
@@ -2027,8 +2030,11 @@ class SQLiteAdapter(BaseAdapter):
'second': (17, 19), 'second': (17, 19),
} }
try: try:
(i, j) = table[lookup] if lookup != 'epoch':
return int(s[i:j]) (i, j) = table[lookup]
return int(s[i:j])
else:
return time.mktime(datetime.datetime.strptime(s, '%Y-%m-%d %H:%M:%S').timetuple())
except: except:
return None return None
@@ -2280,6 +2286,9 @@ class MySQLAdapter(BaseAdapter):
return 'SUBSTRING(%s,%s,%s)' % (self.expand(field), return 'SUBSTRING(%s,%s,%s)' % (self.expand(field),
parameters[0], parameters[1]) parameters[0], parameters[1])
def EPOCH(self, first):
return "UNIX_TIMESTAMP(%s)" % self.expand(first)
def _drop(self,table,mode): def _drop(self,table,mode):
# breaks db integrity but without this mysql does not drop table # breaks db integrity but without this mysql does not drop table
return ['SET FOREIGN_KEY_CHECKS=0;','DROP TABLE %s;' % table, return ['SET FOREIGN_KEY_CHECKS=0;','DROP TABLE %s;' % table,
@@ -2984,7 +2993,7 @@ class MSSQLAdapter(BaseAdapter):
# (in the form of arg1=value1&arg2=value2&...) # (in the form of arg1=value1&arg2=value2&...)
# Default values (drivers like FreeTDS insist on uppercase parameter keys) # Default values (drivers like FreeTDS insist on uppercase parameter keys)
argsdict = { 'DRIVER':'{SQL Server}' } argsdict = { 'DRIVER':'{SQL Server}' }
urlargs = m.group('urlargs') or '' urlargs = m.group('urlargs') or ''
for argmatch in self.REGEX_ARGPATTERN.finditer(urlargs): for argmatch in self.REGEX_ARGPATTERN.finditer(urlargs):
argsdict[str(argmatch.group('argkey')).upper()] = argmatch.group('argvalue') argsdict[str(argmatch.group('argkey')).upper()] = argmatch.group('argvalue')
urlargs = ';'.join(['%s=%s' % (ak, av) for (ak, av) in argsdict.iteritems()]) urlargs = ';'.join(['%s=%s' % (ak, av) for (ak, av) in argsdict.iteritems()])
@@ -3009,6 +3018,9 @@ class MSSQLAdapter(BaseAdapter):
return rows[minimum:] return rows[minimum:]
return rows[minimum:maximum] return rows[minimum:maximum]
def EPOCH(self, first):
return "DATEDIFF(second, '1970-01-01 00:00:00', %s)" % self.expand(first)
# GIS functions # GIS functions
# No STAsGeoJSON in MSSQL # No STAsGeoJSON in MSSQL
@@ -3156,7 +3168,7 @@ class SybaseAdapter(MSSQLAdapter):
logger.error('NdGpatch error') logger.error('NdGpatch error')
raise e raise e
else: else:
m = self.REGEX_URI.match(uri) m = self.REGEX_URI.match(uri)
if not m: if not m:
raise SyntaxError, \ raise SyntaxError, \
"Invalid URI string in DAL: %s" % self.uri "Invalid URI string in DAL: %s" % self.uri
@@ -3228,6 +3240,9 @@ class FireBirdAdapter(BaseAdapter):
def RANDOM(self): def RANDOM(self):
return 'RAND()' return 'RAND()'
def EPOCH(self, first):
return "DATEDIFF(second, '1970-01-01 00:00:00', %s)" % self.expand(first)
def NOT_NULL(self,default,field_type): def NOT_NULL(self,default,field_type):
return 'DEFAULT %s NOT NULL' % self.represent(default,field_type) return 'DEFAULT %s NOT NULL' % self.represent(default,field_type)
@@ -3252,7 +3267,7 @@ class FireBirdAdapter(BaseAdapter):
def __init__(self,db,uri,pool_size=0,folder=None,db_codec ='UTF-8', def __init__(self,db,uri,pool_size=0,folder=None,db_codec ='UTF-8',
credential_decoder=IDENTITY, driver_args={}, credential_decoder=IDENTITY, driver_args={},
adapter_args={}): adapter_args={}):
self.db = db self.db = db
self.dbengine = "firebird" self.dbengine = "firebird"
self.uri = uri self.uri = uri
@@ -3283,7 +3298,7 @@ class FireBirdAdapter(BaseAdapter):
user = credential_decoder(user), user = credential_decoder(user),
password = credential_decoder(password), password = credential_decoder(password),
charset = charset) charset = charset)
def connect(driver_args=driver_args): def connect(driver_args=driver_args):
return self.driver.connect(**driver_args) return self.driver.connect(**driver_args)
self.pool_connection(connect) self.pool_connection(connect)
@@ -3343,7 +3358,7 @@ class FireBirdEmbeddedAdapter(FireBirdAdapter):
user=credential_decoder(user), user=credential_decoder(user),
password=credential_decoder(password), password=credential_decoder(password),
charset=charset) charset=charset)
def connect(driver_args=driver_args): def connect(driver_args=driver_args):
return self.driver.connect(**driver_args) return self.driver.connect(**driver_args)
self.pool_connection(connect) self.pool_connection(connect)
@@ -4416,7 +4431,7 @@ class GoogleDatastoreAdapter(NoSQLAdapter):
else: else:
projection.append(f.name) projection.append(f.name)
# projection's can't include 'id'. # projection's can't include 'id'.
# it will be added to the result later # it will be added to the result later
query_projection = [ query_projection = [
p for p in projection if \ p for p in projection if \
@@ -4912,7 +4927,7 @@ class MongoDBAdapter(NoSQLAdapter):
print "in expand and this is a query" print "in expand and this is a query"
# any query using 'id':= # any query using 'id':=
# set name as _id (as per pymongo/mongodb primary key) # set name as _id (as per pymongo/mongodb primary key)
# convert second arg to an objectid field # convert second arg to an objectid field
# (if its not already) # (if its not already)
# if second arg is 0 convert to objectid # if second arg is 0 convert to objectid
if isinstance(expression.first,Field) and \ if isinstance(expression.first,Field) and \
@@ -4922,9 +4937,9 @@ class MongoDBAdapter(NoSQLAdapter):
not isinstance(expression.second,ObjectId): not isinstance(expression.second,ObjectId):
if isinstance(expression.second,int): if isinstance(expression.second,int):
try: try:
# Because the reference field is by default # Because the reference field is by default
# an integer and therefore this must be an # an integer and therefore this must be an
# integer to be able to work with other # integer to be able to work with other
# databases # databases
expression.second = ObjectId(("%X" % expression.second)) expression.second = ObjectId(("%X" % expression.second))
except: except:
@@ -5212,27 +5227,27 @@ class MongoDBAdapter(NoSQLAdapter):
return result return result
def ADD(self, first, second): def ADD(self, first, second):
raise NotImplementedError, "This must yet be replaced with javescript in order to accomplish this. Sorry" raise NotImplementedError, "This must yet be replaced with javascript in order to accomplish this. Sorry"
return '%s + %s' % (self.expand(first), self.expand(second, first.type)) return '%s + %s' % (self.expand(first), self.expand(second, first.type))
def SUB(self, first, second): def SUB(self, first, second):
raise NotImplementedError, "This must yet be replaced with javescript in order to accomplish this. Sorry" raise NotImplementedError, "This must yet be replaced with javascript in order to accomplish this. Sorry"
return '(%s - %s)' % (self.expand(first), self.expand(second, first.type)) return '(%s - %s)' % (self.expand(first), self.expand(second, first.type))
def MUL(self, first, second): def MUL(self, first, second):
raise NotImplementedError, "This must yet be replaced with javescript in order to accomplish this. Sorry" raise NotImplementedError, "This must yet be replaced with javascript in order to accomplish this. Sorry"
return '(%s * %s)' % (self.expand(first), self.expand(second, first.type)) return '(%s * %s)' % (self.expand(first), self.expand(second, first.type))
def DIV(self, first, second): def DIV(self, first, second):
raise NotImplementedError, "This must yet be replaced with javescript in order to accomplish this. Sorry" raise NotImplementedError, "This must yet be replaced with javascript in order to accomplish this. Sorry"
return '(%s / %s)' % (self.expand(first), self.expand(second, first.type)) return '(%s / %s)' % (self.expand(first), self.expand(second, first.type))
def MOD(self, first, second): def MOD(self, first, second):
raise NotImplementedError, "This must yet be replaced with javescript in order to accomplish this. Sorry" raise NotImplementedError, "This must yet be replaced with javascript in order to accomplish this. Sorry"
return '(%s %% %s)' % (self.expand(first), self.expand(second, first.type)) return '(%s %% %s)' % (self.expand(first), self.expand(second, first.type))
def AS(self, first, second): def AS(self, first, second):
raise NotImplementedError, "This must yet be replaced with javescript in order to accomplish this. Sorry" raise NotImplementedError, "This must yet be replaced with javascript in order to accomplish this. Sorry"
return '%s AS %s' % (self.expand(first), second) return '%s AS %s' % (self.expand(first), second)
#We could implement an option that simulates a full featured SQL database. But I think the option should be set explicit or implemented as another library. #We could implement an option that simulates a full featured SQL database. But I think the option should be set explicit or implemented as another library.
@@ -5344,31 +5359,31 @@ class MongoDBAdapter(NoSQLAdapter):
#TODO javascript has math #TODO javascript has math
def ADD(self, first, second): def ADD(self, first, second):
raise NotImplementedError, "This must yet be replaced with javescript in order to accomplish this. Sorry" raise NotImplementedError, "This must yet be replaced with javascript in order to accomplish this. Sorry"
return '%s + %s' % (self.expand(first), self.expand(second, first.type)) return '%s + %s' % (self.expand(first), self.expand(second, first.type))
#TODO javascript has math #TODO javascript has math
def SUB(self, first, second): def SUB(self, first, second):
raise NotImplementedError, "This must yet be replaced with javescript in order to accomplish this. Sorry" raise NotImplementedError, "This must yet be replaced with javascript in order to accomplish this. Sorry"
return '(%s - %s)' % (self.expand(first), self.expand(second, first.type)) return '(%s - %s)' % (self.expand(first), self.expand(second, first.type))
#TODO javascript has math #TODO javascript has math
def MUL(self, first, second): def MUL(self, first, second):
raise NotImplementedError, "This must yet be replaced with javescript in order to accomplish this. Sorry" raise NotImplementedError, "This must yet be replaced with javascript in order to accomplish this. Sorry"
return '(%s * %s)' % (self.expand(first), self.expand(second, first.type)) return '(%s * %s)' % (self.expand(first), self.expand(second, first.type))
#TODO javascript has math #TODO javascript has math
def DIV(self, first, second): def DIV(self, first, second):
raise NotImplementedError, "This must yet be replaced with javescript in order to accomplish this. Sorry" raise NotImplementedError, "This must yet be replaced with javascript in order to accomplish this. Sorry"
return '(%s / %s)' % (self.expand(first), self.expand(second, first.type)) return '(%s / %s)' % (self.expand(first), self.expand(second, first.type))
#TODO javascript has math #TODO javascript has math
def MOD(self, first, second): def MOD(self, first, second):
raise NotImplementedError, "This must yet be replaced with javescript in order to accomplish this. Sorry" raise NotImplementedError, "This must yet be replaced with javascript in order to accomplish this. Sorry"
return '(%s %% %s)' % (self.expand(first), self.expand(second, first.type)) return '(%s %% %s)' % (self.expand(first), self.expand(second, first.type))
#TODO javascript can do this #TODO javascript can do this
def AS(self, first, second): def AS(self, first, second):
raise NotImplementedError, "This must yet be replaced with javescript in order to accomplish this. Sorry" raise NotImplementedError, "This must yet be replaced with javascript in order to accomplish this. Sorry"
return '%s AS %s' % (self.expand(first), second) return '%s AS %s' % (self.expand(first), second)
#We could implement an option that simulates a full featured SQL database. But I think the option should be set explicit or implemented as another library. #We could implement an option that simulates a full featured SQL database. But I think the option should be set explicit or implemented as another library.
@@ -6455,7 +6470,7 @@ class Row(object):
def values(self): def values(self):
return self.__dict__.values() return self.__dict__.values()
def __iter__(self): def __iter__(self):
return self.__dict__.__iter__() return self.__dict__.__iter__()
@@ -6648,7 +6663,7 @@ class DAL(object):
db.define_table('tablename', Field('fieldname1'), db.define_table('tablename', Field('fieldname1'),
Field('fieldname2')) Field('fieldname2'))
""" """
@staticmethod @staticmethod
def set_folder(folder): def set_folder(folder):
""" """
@@ -7211,7 +7226,7 @@ def index():
Added 2012-08-24 "fields" and "colnames" optional arguments. If either Added 2012-08-24 "fields" and "colnames" optional arguments. If either
is provided, the results cursor returned by the DB driver will be is provided, the results cursor returned by the DB driver will be
converted to a DAL Rows object using the db._adapter.parse() method. converted to a DAL Rows object using the db._adapter.parse() method.
The "fields" argument is a list of DAL Field objects that match the The "fields" argument is a list of DAL Field objects that match the
fields returned from the DB. The Field objects should be part of one or fields returned from the DB. The Field objects should be part of one or
more Table objects defined on the DAL object. The "fields" list can more Table objects defined on the DAL object. The "fields" list can
@@ -7224,14 +7239,14 @@ def index():
can be specified as a list of field names in tablename.fieldname format. can be specified as a list of field names in tablename.fieldname format.
Again, these should represent tables and fields defined on the DAL Again, these should represent tables and fields defined on the DAL
object. object.
It is also possible to specify both "fields" and the associated It is also possible to specify both "fields" and the associated
"colnames". In that case, "fields" can also include DAL Expression "colnames". In that case, "fields" can also include DAL Expression
objects in addition to Field objects. For Field objects in "fields", objects in addition to Field objects. For Field objects in "fields",
the associated "colnames" must still be in tablename.fieldname format. the associated "colnames" must still be in tablename.fieldname format.
For Expression objects in "fields", the associated "colnames" can For Expression objects in "fields", the associated "colnames" can
be any arbitrary labels. be any arbitrary labels.
Note, the DAL Table objects referred to by "fields" or "colnames" can Note, the DAL Table objects referred to by "fields" or "colnames" can
be dummy tables and do not have to represent any real tables in the be dummy tables and do not have to represent any real tables in the
database. Also, note that the "fields" and "colnames" must be in the database. Also, note that the "fields" and "colnames" must be in the
@@ -7390,7 +7405,7 @@ class Table(object):
db.users.insert(name='me') # print db.users._insert(...) to see SQL db.users.insert(name='me') # print db.users._insert(...) to see SQL
db.users.drop() db.users.drop()
""" """
def __init__( def __init__(
self, self,
db, db,
@@ -8075,6 +8090,10 @@ class Expression(object):
db = self.db db = self.db
return Expression(db, db._adapter.EXTRACT, self, 'second', 'integer') return Expression(db, db._adapter.EXTRACT, self, 'second', 'integer')
def epoch(self):
db = self.db
return Expression(db, db._adapter.EPOCH, self, None, 'integer')
def __getslice__(self, start, stop): def __getslice__(self, start, stop):
db = self.db db = self.db
if start < 0: if start < 0:
@@ -8215,7 +8234,7 @@ class Expression(object):
def st_asgeojson(self, precision=15, options=0, version=1): def st_asgeojson(self, precision=15, options=0, version=1):
return Expression(self.db, self.db._adapter.ST_ASGEOJSON, self, return Expression(self.db, self.db._adapter.ST_ASGEOJSON, self,
dict(precision=precision, options=options, dict(precision=precision, options=options,
version=version), 'dict') version=version), 'dict')
def st_astext(self): def st_astext(self):
@@ -8464,7 +8483,7 @@ class Field(Expression):
elif not filename: elif not filename:
filename = file.name filename = file.name
filename = os.path.basename(filename.replace('/', os.sep)\ filename = os.path.basename(filename.replace('/', os.sep)\
.replace('\\', os.sep)) .replace('\\', os.sep))
m = REGEX_STORE_PATTERN.search(filename) m = REGEX_STORE_PATTERN.search(filename)
extension = m and m.group('e') or 'txt' extension = m and m.group('e') or 'txt'
uuid_key = web2py_uuid().replace('-', '')[-16:] uuid_key = web2py_uuid().replace('-', '')[-16:]
@@ -8752,7 +8771,7 @@ class Set(object):
key, key,
(lambda self=self,distinct=distinct: \ (lambda self=self,distinct=distinct: \
db._adapter.count(self.query,distinct)), db._adapter.count(self.query,distinct)),
time_expire) time_expire)
return db._adapter.count(self.query,distinct) return db._adapter.count(self.query,distinct)
def select(self, *fields, **attributes): def select(self, *fields, **attributes):
@@ -8870,7 +8889,7 @@ class RecordUpdater(object):
self.colset, self.table, self.id = colset, table, id self.colset, self.table, self.id = colset, table, id
def __call__(self, **fields): def __call__(self, **fields):
colset, table, id = self.colset, self.table, self.id colset, table, id = self.colset, self.table, self.id
newfields = fields or dict(colset) newfields = fields or dict(colset)
for fieldname in newfields.keys(): for fieldname in newfields.keys():
if not fieldname in table.fields or table[fieldname].type=='id': if not fieldname in table.fields or table[fieldname].type=='id':
@@ -9192,7 +9211,7 @@ class Rows(object):
def xml(self,strict=False,row_name='row',rows_name='rows'): def xml(self,strict=False,row_name='row',rows_name='rows'):
""" """
serializes the table using sqlhtml.SQLTABLE (if present) serializes the table using sqlhtml.SQLTABLE (if present)
""" """
if strict: if strict:
ncols = len(self.colnames) ncols = len(self.colnames)
def f(row,field,indent=' '): def f(row,field,indent=' '):
+8 -7
View File
@@ -311,12 +311,13 @@ class TestDatetime(unittest.TestCase):
9, 30)), 3) 9, 30)), 3)
self.assertEqual(len(db(db.t.a == datetime.datetime(1971, 12, self.assertEqual(len(db(db.t.a == datetime.datetime(1971, 12,
21, 11, 30)).select()), 1) 21, 11, 30)).select()), 1)
self.assertEqual(len(db(db.t.a.year() == 1971).select()), 2) self.assertEqual(db(db.t.a.year() == 1971).count(), 2)
self.assertEqual(len(db(db.t.a.month() == 12).select()), 2) self.assertEqual(db(db.t.a.month() == 12).count(), 2)
self.assertEqual(len(db(db.t.a.day() == 21).select()), 3) self.assertEqual(db(db.t.a.day() == 21).count(), 3)
self.assertEqual(len(db(db.t.a.hour() == 11).select()), 1) self.assertEqual(db(db.t.a.hour() == 11).count(), 1)
self.assertEqual(len(db(db.t.a.minutes() == 30).select()), 3) self.assertEqual(db(db.t.a.minutes() == 30).count(), 3)
self.assertEqual(len(db(db.t.a.seconds() == 0).select()), 3) self.assertEqual(db(db.t.a.seconds() == 0).count(), 3)
self.assertEqual(db(db.t.a.epoch()<365*24*3600).count(),1)
db.t.drop() db.t.drop()
@@ -329,7 +330,7 @@ class TestExpressions(unittest.TestCase):
self.assertEqual(db.t.insert(a=2), 2) self.assertEqual(db.t.insert(a=2), 2)
self.assertEqual(db.t.insert(a=3), 3) self.assertEqual(db.t.insert(a=3), 3)
self.assertEqual(db(db.t.a == 3).update(a=db.t.a + 1), 1) self.assertEqual(db(db.t.a == 3).update(a=db.t.a + 1), 1)
self.assertEqual(len(db(db.t.a == 4).select()), 1) self.assertEqual(db(db.t.a == 4).count(), 1)
db.t.drop() db.t.drop()