db.t.f.epoch(), thanks Niphlod
This commit is contained in:
@@ -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
@@ -40,7 +40,7 @@ Example of usage:
|
||||
>>> # from dal import DAL, Field
|
||||
|
||||
### 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)
|
||||
|
||||
### define a table 'person' (create/alter as necessary)
|
||||
@@ -330,7 +330,7 @@ if not 'google' in DRIVERS:
|
||||
DRIVERS.append('Sybase(Sybase)')
|
||||
except ImportError:
|
||||
logger.debug('no Sybase driver')
|
||||
|
||||
|
||||
try:
|
||||
import kinterbasdb
|
||||
DRIVERS.append('Interbase(kinterbasdb)')
|
||||
@@ -342,7 +342,7 @@ if not 'google' in DRIVERS:
|
||||
import fdb
|
||||
DRIVERS.append('Firbird(fdb)')
|
||||
except ImportError:
|
||||
logger.debug('no Firebird driver fdb')
|
||||
logger.debug('no Firebird driver fdb')
|
||||
#####
|
||||
try:
|
||||
import firebirdsql
|
||||
@@ -660,7 +660,7 @@ class BaseAdapter(ConnectionPool):
|
||||
|
||||
def find_driver(self,adapter_args,uri=None):
|
||||
if hasattr(self,'driver') and self.driver!=None:
|
||||
return
|
||||
return
|
||||
drivers_available = [driver for driver in self.drivers
|
||||
if driver in globals()]
|
||||
if uri:
|
||||
@@ -668,7 +668,7 @@ class BaseAdapter(ConnectionPool):
|
||||
request_driver = items[1] if len(items)>1 else None
|
||||
else:
|
||||
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 in drivers_available:
|
||||
self.driver_name = request_driver
|
||||
@@ -680,7 +680,7 @@ class BaseAdapter(ConnectionPool):
|
||||
self.driver = globals().get(self.driver_name)
|
||||
else:
|
||||
raise RuntimeError, "no driver available %s", self.drivers
|
||||
|
||||
|
||||
|
||||
def __init__(self, db,uri,pool_size=0, folder=None, db_codec='UTF-8',
|
||||
credential_decoder=IDENTITY, driver_args={},
|
||||
@@ -799,7 +799,7 @@ class BaseAdapter(ConnectionPool):
|
||||
else:
|
||||
schema = parms[0]
|
||||
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,
|
||||
fieldname=field_name, srid=srid,
|
||||
dimension=dimension)
|
||||
@@ -890,7 +890,7 @@ class BaseAdapter(ConnectionPool):
|
||||
else:
|
||||
table._dbt = pjoin(
|
||||
dbpath, '%s_%s.table' % (table._db._uri_hash, tablename))
|
||||
|
||||
|
||||
if table._dbt:
|
||||
table._loggername = pjoin(dbpath, 'sql.log')
|
||||
logfile = self.file_open(table._loggername, 'a')
|
||||
@@ -1002,7 +1002,7 @@ class BaseAdapter(ConnectionPool):
|
||||
query = ['ALTER TABLE %s DROP %s;' % (tablename, key)]
|
||||
metadata_change = True
|
||||
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)) \
|
||||
and not sql_fields[key]['type'].startswith('reference')\
|
||||
and not sql_fields[key]['type'].startswith('double')\
|
||||
@@ -1073,6 +1073,9 @@ class BaseAdapter(ConnectionPool):
|
||||
def EXTRACT(self, first, what):
|
||||
return "EXTRACT(%s FROM %s)" % (what, self.expand(first))
|
||||
|
||||
def EPOCH(self, first):
|
||||
return self.EXTRACT(first, 'epoch')
|
||||
|
||||
def AGGREGATE(self, first, what):
|
||||
return "%s(%s)" % (what, self.expand(first))
|
||||
|
||||
@@ -1273,7 +1276,7 @@ class BaseAdapter(ConnectionPool):
|
||||
first = expression.first
|
||||
second = expression.second
|
||||
op = expression.op
|
||||
if not second is None:
|
||||
if not second is None:
|
||||
return op(first, second)
|
||||
elif not first is None:
|
||||
return op(first)
|
||||
@@ -1580,14 +1583,14 @@ class BaseAdapter(ConnectionPool):
|
||||
"""
|
||||
sql = self._select(query, fields, attributes)
|
||||
cache = attributes.get('cache', None)
|
||||
if cache and attributes.get('cacheable',False):
|
||||
if cache and attributes.get('cacheable',False):
|
||||
del attributes['cache']
|
||||
(cache_model, time_expire) = cache
|
||||
key = self.uri + '/' + sql
|
||||
if len(key)>200: key = hashlib.md5(key).hexdigest()
|
||||
args = (sql,fields,attributes)
|
||||
return cache_model(
|
||||
key,
|
||||
key,
|
||||
lambda self=self,args=args:self._select_aux(*args),
|
||||
time_expire)
|
||||
else:
|
||||
@@ -1906,7 +1909,7 @@ class BaseAdapter(ConnectionPool):
|
||||
for (i,row) in enumerate(rows):
|
||||
new_row = Row()
|
||||
for (j,colname) in enumerate(colnames):
|
||||
value = row[j]
|
||||
value = row[j]
|
||||
tmp = tmps[j]
|
||||
if tmp:
|
||||
(tablename,fieldname,table,field,ft) = tmp
|
||||
@@ -1920,14 +1923,14 @@ class BaseAdapter(ConnectionPool):
|
||||
if field.filter_out:
|
||||
value = field.filter_out(value)
|
||||
colset[fieldname] = value
|
||||
|
||||
|
||||
# for backward compatibility
|
||||
if ft=='id' and fieldname!='id' and \
|
||||
not 'id' in table.fields:
|
||||
colset['id'] = value
|
||||
|
||||
if ft == 'id' and not cacheable:
|
||||
# temporary hack to deal with
|
||||
# temporary hack to deal with
|
||||
# GoogleDatastoreAdapter
|
||||
# references
|
||||
if isinstance(self, GoogleDatastoreAdapter):
|
||||
@@ -2027,8 +2030,11 @@ class SQLiteAdapter(BaseAdapter):
|
||||
'second': (17, 19),
|
||||
}
|
||||
try:
|
||||
(i, j) = table[lookup]
|
||||
return int(s[i:j])
|
||||
if lookup != 'epoch':
|
||||
(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:
|
||||
return None
|
||||
|
||||
@@ -2280,6 +2286,9 @@ class MySQLAdapter(BaseAdapter):
|
||||
return 'SUBSTRING(%s,%s,%s)' % (self.expand(field),
|
||||
parameters[0], parameters[1])
|
||||
|
||||
def EPOCH(self, first):
|
||||
return "UNIX_TIMESTAMP(%s)" % self.expand(first)
|
||||
|
||||
def _drop(self,table,mode):
|
||||
# breaks db integrity but without this mysql does not drop 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&...)
|
||||
# Default values (drivers like FreeTDS insist on uppercase parameter keys)
|
||||
argsdict = { 'DRIVER':'{SQL Server}' }
|
||||
urlargs = m.group('urlargs') or ''
|
||||
urlargs = m.group('urlargs') or ''
|
||||
for argmatch in self.REGEX_ARGPATTERN.finditer(urlargs):
|
||||
argsdict[str(argmatch.group('argkey')).upper()] = argmatch.group('argvalue')
|
||||
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:maximum]
|
||||
|
||||
def EPOCH(self, first):
|
||||
return "DATEDIFF(second, '1970-01-01 00:00:00', %s)" % self.expand(first)
|
||||
|
||||
# GIS functions
|
||||
|
||||
# No STAsGeoJSON in MSSQL
|
||||
@@ -3156,7 +3168,7 @@ class SybaseAdapter(MSSQLAdapter):
|
||||
logger.error('NdGpatch error')
|
||||
raise e
|
||||
else:
|
||||
m = self.REGEX_URI.match(uri)
|
||||
m = self.REGEX_URI.match(uri)
|
||||
if not m:
|
||||
raise SyntaxError, \
|
||||
"Invalid URI string in DAL: %s" % self.uri
|
||||
@@ -3228,6 +3240,9 @@ class FireBirdAdapter(BaseAdapter):
|
||||
def RANDOM(self):
|
||||
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):
|
||||
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',
|
||||
credential_decoder=IDENTITY, driver_args={},
|
||||
adapter_args={}):
|
||||
adapter_args={}):
|
||||
self.db = db
|
||||
self.dbengine = "firebird"
|
||||
self.uri = uri
|
||||
@@ -3283,7 +3298,7 @@ class FireBirdAdapter(BaseAdapter):
|
||||
user = credential_decoder(user),
|
||||
password = credential_decoder(password),
|
||||
charset = charset)
|
||||
|
||||
|
||||
def connect(driver_args=driver_args):
|
||||
return self.driver.connect(**driver_args)
|
||||
self.pool_connection(connect)
|
||||
@@ -3343,7 +3358,7 @@ class FireBirdEmbeddedAdapter(FireBirdAdapter):
|
||||
user=credential_decoder(user),
|
||||
password=credential_decoder(password),
|
||||
charset=charset)
|
||||
|
||||
|
||||
def connect(driver_args=driver_args):
|
||||
return self.driver.connect(**driver_args)
|
||||
self.pool_connection(connect)
|
||||
@@ -4416,7 +4431,7 @@ class GoogleDatastoreAdapter(NoSQLAdapter):
|
||||
else:
|
||||
projection.append(f.name)
|
||||
|
||||
# projection's can't include 'id'.
|
||||
# projection's can't include 'id'.
|
||||
# it will be added to the result later
|
||||
query_projection = [
|
||||
p for p in projection if \
|
||||
@@ -4912,7 +4927,7 @@ class MongoDBAdapter(NoSQLAdapter):
|
||||
print "in expand and this is a query"
|
||||
# any query using 'id':=
|
||||
# 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 second arg is 0 convert to objectid
|
||||
if isinstance(expression.first,Field) and \
|
||||
@@ -4922,9 +4937,9 @@ class MongoDBAdapter(NoSQLAdapter):
|
||||
not isinstance(expression.second,ObjectId):
|
||||
if isinstance(expression.second,int):
|
||||
try:
|
||||
# Because the reference field is by default
|
||||
# an integer and therefore this must be an
|
||||
# integer to be able to work with other
|
||||
# Because the reference field is by default
|
||||
# an integer and therefore this must be an
|
||||
# integer to be able to work with other
|
||||
# databases
|
||||
expression.second = ObjectId(("%X" % expression.second))
|
||||
except:
|
||||
@@ -5212,27 +5227,27 @@ class MongoDBAdapter(NoSQLAdapter):
|
||||
return result
|
||||
|
||||
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))
|
||||
|
||||
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))
|
||||
|
||||
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))
|
||||
|
||||
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))
|
||||
|
||||
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))
|
||||
|
||||
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)
|
||||
|
||||
#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
|
||||
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))
|
||||
|
||||
#TODO javascript has math
|
||||
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))
|
||||
|
||||
#TODO javascript has math
|
||||
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))
|
||||
#TODO javascript has math
|
||||
|
||||
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))
|
||||
#TODO javascript has math
|
||||
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))
|
||||
|
||||
#TODO javascript can do this
|
||||
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)
|
||||
|
||||
#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):
|
||||
return self.__dict__.values()
|
||||
|
||||
|
||||
def __iter__(self):
|
||||
return self.__dict__.__iter__()
|
||||
|
||||
@@ -6648,7 +6663,7 @@ class DAL(object):
|
||||
db.define_table('tablename', Field('fieldname1'),
|
||||
Field('fieldname2'))
|
||||
"""
|
||||
|
||||
|
||||
@staticmethod
|
||||
def set_folder(folder):
|
||||
"""
|
||||
@@ -7211,7 +7226,7 @@ def index():
|
||||
Added 2012-08-24 "fields" and "colnames" optional arguments. If either
|
||||
is provided, the results cursor returned by the DB driver will be
|
||||
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
|
||||
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
|
||||
@@ -7224,14 +7239,14 @@ def index():
|
||||
can be specified as a list of field names in tablename.fieldname format.
|
||||
Again, these should represent tables and fields defined on the DAL
|
||||
object.
|
||||
|
||||
|
||||
It is also possible to specify both "fields" and the associated
|
||||
"colnames". In that case, "fields" can also include DAL Expression
|
||||
objects in addition to Field objects. For Field objects in "fields",
|
||||
the associated "colnames" must still be in tablename.fieldname format.
|
||||
For Expression objects in "fields", the associated "colnames" can
|
||||
be any arbitrary labels.
|
||||
|
||||
|
||||
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
|
||||
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.drop()
|
||||
"""
|
||||
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
db,
|
||||
@@ -8075,6 +8090,10 @@ class Expression(object):
|
||||
db = self.db
|
||||
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):
|
||||
db = self.db
|
||||
if start < 0:
|
||||
@@ -8215,7 +8234,7 @@ class Expression(object):
|
||||
|
||||
def st_asgeojson(self, precision=15, options=0, version=1):
|
||||
return Expression(self.db, self.db._adapter.ST_ASGEOJSON, self,
|
||||
dict(precision=precision, options=options,
|
||||
dict(precision=precision, options=options,
|
||||
version=version), 'dict')
|
||||
|
||||
def st_astext(self):
|
||||
@@ -8464,7 +8483,7 @@ class Field(Expression):
|
||||
elif not filename:
|
||||
filename = file.name
|
||||
filename = os.path.basename(filename.replace('/', os.sep)\
|
||||
.replace('\\', os.sep))
|
||||
.replace('\\', os.sep))
|
||||
m = REGEX_STORE_PATTERN.search(filename)
|
||||
extension = m and m.group('e') or 'txt'
|
||||
uuid_key = web2py_uuid().replace('-', '')[-16:]
|
||||
@@ -8752,7 +8771,7 @@ class Set(object):
|
||||
key,
|
||||
(lambda self=self,distinct=distinct: \
|
||||
db._adapter.count(self.query,distinct)),
|
||||
time_expire)
|
||||
time_expire)
|
||||
return db._adapter.count(self.query,distinct)
|
||||
|
||||
def select(self, *fields, **attributes):
|
||||
@@ -8870,7 +8889,7 @@ class RecordUpdater(object):
|
||||
self.colset, self.table, self.id = colset, table, id
|
||||
|
||||
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)
|
||||
for fieldname in newfields.keys():
|
||||
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'):
|
||||
"""
|
||||
serializes the table using sqlhtml.SQLTABLE (if present)
|
||||
"""
|
||||
"""
|
||||
if strict:
|
||||
ncols = len(self.colnames)
|
||||
def f(row,field,indent=' '):
|
||||
|
||||
@@ -311,12 +311,13 @@ class TestDatetime(unittest.TestCase):
|
||||
9, 30)), 3)
|
||||
self.assertEqual(len(db(db.t.a == datetime.datetime(1971, 12,
|
||||
21, 11, 30)).select()), 1)
|
||||
self.assertEqual(len(db(db.t.a.year() == 1971).select()), 2)
|
||||
self.assertEqual(len(db(db.t.a.month() == 12).select()), 2)
|
||||
self.assertEqual(len(db(db.t.a.day() == 21).select()), 3)
|
||||
self.assertEqual(len(db(db.t.a.hour() == 11).select()), 1)
|
||||
self.assertEqual(len(db(db.t.a.minutes() == 30).select()), 3)
|
||||
self.assertEqual(len(db(db.t.a.seconds() == 0).select()), 3)
|
||||
self.assertEqual(db(db.t.a.year() == 1971).count(), 2)
|
||||
self.assertEqual(db(db.t.a.month() == 12).count(), 2)
|
||||
self.assertEqual(db(db.t.a.day() == 21).count(), 3)
|
||||
self.assertEqual(db(db.t.a.hour() == 11).count(), 1)
|
||||
self.assertEqual(db(db.t.a.minutes() == 30).count(), 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()
|
||||
|
||||
|
||||
@@ -329,7 +330,7 @@ class TestExpressions(unittest.TestCase):
|
||||
self.assertEqual(db.t.insert(a=2), 2)
|
||||
self.assertEqual(db.t.insert(a=3), 3)
|
||||
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()
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user