Merge ssh://github.com/web2py/web2py
This commit is contained in:
@@ -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
@@ -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
|
||||
################################################################################
|
||||
|
||||
@@ -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
@@ -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))
|
||||
|
||||
Reference in New Issue
Block a user