Merge ssh://github.com/web2py/web2py

This commit is contained in:
Michele Comitini
2013-07-22 10:38:28 +02:00
4 changed files with 77 additions and 61 deletions
+1 -1
View File
@@ -1 +1 @@
Version 2.6.0-development+timestamp.2013.07.20.12.59.52
Version 2.6.0-development+timestamp.2013.07.21.17.14.46
+67 -59
View File
@@ -640,6 +640,8 @@ class BaseAdapter(ConnectionPool):
support_distributed_transaction = False
uploads_in_blob = False
can_select_for_update = True
dbpath = None
folder = None
TRUE = 'T'
FALSE = 'F'
@@ -733,6 +735,29 @@ class BaseAdapter(ConnectionPool):
else:
raise RuntimeError("no driver available %s" % str(self.drivers))
def log(self, message, table=None):
""" Logs migrations
It will not log changes if logfile is not specified. Defaults
to sql.log
"""
isabs = None
logfilename = self.adapter_args.get('logfile','sql.log')
writelog = bool(logfilename)
if writelog:
isabs = os.path.isabs(logfilename)
if table and table._dbt and writelog and self.folder:
if isabs:
table._loggername = logfilename
else:
table._loggername = pjoin(self.folder, logfilename)
logfile = self.file_open(table._loggername, 'a')
logfile.write(message)
self.file_close(logfile)
def __init__(self, db,uri,pool_size=0, folder=None, db_codec='UTF-8',
credential_decoder=IDENTITY, driver_args={},
adapter_args={},do_connect=True, after_connection=None):
@@ -950,17 +975,11 @@ class BaseAdapter(ConnectionPool):
table._dbt = pjoin(
dbpath, '%s_%s.table' % (table._db._uri_hash, tablename))
if table._dbt:
logfilename = self.adapter_args.get('logfile','sql.log')
table._loggername = pjoin(dbpath, logfilename)
logfile = self.file_open(table._loggername, 'a')
else:
logfile = None
if not table._dbt or not self.file_exists(table._dbt):
if table._dbt:
logfile.write('timestamp: %s\n'
% datetime.datetime.today().isoformat())
logfile.write(query + '\n')
self.log('timestamp: %s\n%s\n'
% (datetime.datetime.today().isoformat(),
query), table)
if not fake_migrate:
self.create_sequence_and_triggers(query,table)
table._db.commit()
@@ -974,24 +993,22 @@ class BaseAdapter(ConnectionPool):
pickle.dump(sql_fields, tfile)
self.file_close(tfile)
if fake_migrate:
logfile.write('faked!\n')
self.log('faked!\n', table)
else:
logfile.write('success!\n')
self.log('success!\n', table)
else:
tfile = self.file_open(table._dbt, 'r')
try:
sql_fields_old = pickle.load(tfile)
except EOFError:
self.file_close(tfile)
self.file_close(logfile)
raise RuntimeError('File %s appears corrupted' % table._dbt)
self.file_close(tfile)
if sql_fields != sql_fields_old:
self.migrate_table(table,
sql_fields, sql_fields_old,
sql_fields_aux, logfile,
sql_fields_aux, None,
fake_migrate=fake_migrate)
self.file_close(logfile)
return query
def migrate_table(
@@ -1003,6 +1020,8 @@ class BaseAdapter(ConnectionPool):
logfile,
fake_migrate=False,
):
# logfile is deprecated (moved to adapter.log method)
db = table._db
db._migrated.append(table._tablename)
tablename = table._tablename
@@ -1084,15 +1103,15 @@ class BaseAdapter(ConnectionPool):
metadata_change = True
if query:
logfile.write('timestamp: %s\n'
% datetime.datetime.today().isoformat())
self.log('timestamp: %s\n'
% datetime.datetime.today().isoformat(), table)
db['_lastsql'] = '\n'.join(query)
for sub_query in query:
logfile.write(sub_query + '\n')
self.log(sub_query + '\n', table)
if fake_migrate:
if db._adapter.commit_on_alter_table:
self.save_dbt(table,sql_fields_current)
logfile.write('faked!\n')
self.log('faked!\n', table)
else:
self.execute(sub_query)
# Caveat: mysql, oracle and firebird do not allow multiple alter table
@@ -1101,7 +1120,7 @@ class BaseAdapter(ConnectionPool):
if db._adapter.commit_on_alter_table:
db.commit()
self.save_dbt(table,sql_fields_current)
logfile.write('success!\n')
self.log('success!\n', table)
elif metadata_change:
self.save_dbt(table,sql_fields_current)
@@ -1109,7 +1128,7 @@ class BaseAdapter(ConnectionPool):
if metadata_change and not (query and db._adapter.commit_on_alter_table):
db.commit()
self.save_dbt(table,sql_fields_current)
logfile.write('success!\n')
self.log('success!\n', table)
def save_dbt(self,table, sql_fields_current):
tfile = self.file_open(table._dbt, 'w')
@@ -1174,12 +1193,10 @@ class BaseAdapter(ConnectionPool):
def drop(self, table, mode=''):
db = table._db
if table._dbt:
logfile = self.file_open(table._loggername, 'a')
queries = self._drop(table, mode)
for query in queries:
if table._dbt:
logfile.write(query + '\n')
self.log(query + '\n', table)
self.execute(query)
db.commit()
del db[table._tablename]
@@ -1187,7 +1204,7 @@ class BaseAdapter(ConnectionPool):
db._remove_references_to(table)
if table._dbt:
self.file_delete(table._dbt)
logfile.write('success!\n')
self.log('success!\n', table)
def _insert(self, table, fields):
if fields:
@@ -1423,25 +1440,15 @@ class BaseAdapter(ConnectionPool):
def truncate(self, table, mode= ' '):
# Prepare functions "write_to_logfile" and "close_logfile"
if table._dbt:
logfile = self.file_open(table._loggername, 'a')
else:
class Logfile(object):
def write(self, value):
pass
def close(self):
pass
logfile = Logfile()
try:
queries = table._db._adapter._truncate(table, mode)
for query in queries:
logfile.write(query + '\n')
self.log(query + '\n', table)
self.execute(query)
table._db.commit()
logfile.write('success!\n')
self.log('success!\n', table)
finally:
logfile.close()
pass
def _update(self, tablename, query, fields):
if query:
@@ -2214,20 +2221,20 @@ class SQLiteAdapter(BaseAdapter):
path_encoding = sys.getfilesystemencoding() \
or locale.getdefaultlocale()[1] or 'utf8'
if uri.startswith('sqlite:memory'):
dbpath = ':memory:'
self.dbpath = ':memory:'
else:
dbpath = uri.split('://',1)[1]
if dbpath[0] != '/':
self.dbpath = uri.split('://',1)[1]
if self.dbpath[0] != '/':
if PYTHON_VERSION == 2:
dbpath = pjoin(
self.folder.decode(path_encoding).encode('utf8'), dbpath)
self.dbpath = pjoin(
self.folder.decode(path_encoding).encode('utf8'), self.dbpath)
else:
dbpath = pjoin(self.folder, dbpath)
self.dbpath = pjoin(self.folder, self.dbpath)
if not 'check_same_thread' in driver_args:
driver_args['check_same_thread'] = False
if not 'detect_types' in driver_args and do_connect:
driver_args['detect_types'] = self.driver.PARSE_DECLTYPES
def connector(dbpath=dbpath, driver_args=driver_args):
def connector(dbpath=self.dbpath, driver_args=driver_args):
return self.driver.Connection(dbpath, **driver_args)
self.connector = connector
if do_connect: self.reconnect()
@@ -2282,17 +2289,17 @@ class SpatiaLiteAdapter(SQLiteAdapter):
path_encoding = sys.getfilesystemencoding() \
or locale.getdefaultlocale()[1] or 'utf8'
if uri.startswith('spatialite:memory'):
dbpath = ':memory:'
self.dbpath = ':memory:'
else:
dbpath = uri.split('://',1)[1]
if dbpath[0] != '/':
dbpath = pjoin(
self.folder.decode(path_encoding).encode('utf8'), dbpath)
self.dbpath = uri.split('://',1)[1]
if self.dbpath[0] != '/':
self.dbpath = pjoin(
self.folder.decode(path_encoding).encode('utf8'), self.dbpath)
if not 'check_same_thread' in driver_args:
driver_args['check_same_thread'] = False
if not 'detect_types' in driver_args and do_connect:
driver_args['detect_types'] = self.driver.PARSE_DECLTYPES
def connector(dbpath=dbpath, driver_args=driver_args):
def connector(dbpath=self.dbpath, driver_args=driver_args):
return self.driver.Connection(dbpath, **driver_args)
self.connector = connector
if do_connect: self.reconnect()
@@ -2387,13 +2394,13 @@ class JDBCSQLiteAdapter(SQLiteAdapter):
path_encoding = sys.getfilesystemencoding() \
or locale.getdefaultlocale()[1] or 'utf8'
if uri.startswith('sqlite:memory'):
dbpath = ':memory:'
self.dbpath = ':memory:'
else:
dbpath = uri.split('://',1)[1]
if dbpath[0] != '/':
dbpath = pjoin(
self.folder.decode(path_encoding).encode('utf8'), dbpath)
def connector(dbpath=dbpath,driver_args=driver_args):
self.dbpath = uri.split('://',1)[1]
if self.dbpath[0] != '/':
self.dbpath = pjoin(
self.folder.decode(path_encoding).encode('utf8'), self.dbpath)
def connector(dbpath=self.dbpath,driver_args=driver_args):
return self.driver.connect(
self.driver.getConnection('jdbc:sqlite:'+dbpath),
**driver_args)
@@ -6781,7 +6788,7 @@ def sqlhtml_validators(field):
if field_type in (('string', 'text', 'password')):
requires.append(validators.IS_LENGTH(field_length))
elif field_type == 'json':
requires.append(validators.IS_EMPTY_OR(validators.IS_JSON()))
requires.append(validators.IS_EMPTY_OR(validators.IS_JSON(native_json=field.db._adapter.native_json)))
elif field_type == 'double' or field_type == 'float':
requires.append(validators.IS_FLOAT_IN_RANGE(-1e100, 1e100))
elif field_type in ('integer','bigint'):
@@ -6876,8 +6883,8 @@ class Row(object):
key=str(k)
_extra = self.__dict__.get('_extra', None)
if _extra is not None:
v = _extra.get(key, None)
if v is not None:
v = _extra.get(key, DEFAULT)
if v != DEFAULT:
return v
m = REGEX_TABLE_DOT_FIELD.match(key)
if m:
@@ -10492,6 +10499,7 @@ class Rows(object):
as_csv = __str__
json = as_json
################################################################################
# dummy function used to define some doctests
################################################################################
+4
View File
@@ -387,6 +387,10 @@ class TestExpressions(unittest.TestCase):
self.assertEqual(db.tt.insert(aa=3), 3)
self.assertEqual(db(db.tt.aa == 3).update(aa=db.tt.aa + 1), 1)
self.assertEqual(db(db.tt.aa == 4).count(), 1)
self.assertEqual(db(db.tt.aa == -2).count(), 0)
sum = (db.tt.aa + 1).sum()
self.assertEqual(db(db.tt.aa == 2).select(sum).first()[sum], 3)
self.assertEqual(db(db.tt.aa == -2).select(sum).first()[sum], None)
db.tt.drop()
+5 -1
View File
@@ -346,13 +346,17 @@ class IS_JSON(Validator):
('spam1234', 'invalid json')
"""
def __init__(self, error_message='invalid json'):
def __init__(self, error_message='invalid json', native_json=False):
self.native_json = native_json
self.error_message = error_message
def __call__(self, value):
if value is None:
return None
try:
if self.native_json:
simplejson.dumps(value) # raises error in case of malformed json
return (value, None) # the serialized value is not passed
return (simplejson.loads(value), None)
except JSONErrors:
return (value, translate(self.error_message))