From 75fce87176bf76d6142a5f2539e53eb58041ba98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Fri, 5 Sep 2008 13:25:54 +0200 Subject: [PATCH] Fix ConcurrencyException to be time independant Add new keyword field '_timestamp' Check concurrency based on _timestamp of each ids --- trytond/ir/ui/menu.py | 4 +-- trytond/osv/orm.py | 84 +++++++++++++++++++++++++++---------------- 2 files changed, 55 insertions(+), 33 deletions(-) diff --git a/trytond/ir/ui/menu.py b/trytond/ir/ui/menu.py index a7d3fe240f..cc1aff65b5 100644 --- a/trytond/ir/ui/menu.py +++ b/trytond/ir/ui/menu.py @@ -154,8 +154,8 @@ class UIMenu(OSV): if context is None: context = {} ctx = context.copy() - if 'read_delta' in ctx: - del ctx['read_delta'] + if '_timestamp' in ctx: + del ctx['_timestamp'] action_keyword_obj = self.pool.get('ir.action.keyword') action_keyword_ids = action_keyword_obj.search(cursor, user, [ ('keyword', '=', 'tree_open'), diff --git a/trytond/osv/orm.py b/trytond/osv/orm.py index a47a648bae..25932b9fc5 100644 --- a/trytond/osv/orm.py +++ b/trytond/osv/orm.py @@ -1006,8 +1006,9 @@ class ORM(object): # all inherited fields + all non inherited fields # for which the attribute whose name is in load is True - fields_pre = [x for x in fields_names if x in self._columns \ - and getattr(self._columns[x], '_classic_write')] + \ + fields_pre = [x for x in fields_names if (x in self._columns \ + and getattr(self._columns[x], '_classic_write')) or \ + (x == '_timestamp')] + \ self._inherits.values() res = [] @@ -1019,7 +1020,12 @@ class ORM(object): if len(fields_pre) : fields_pre2 = [(x in ('create_date', 'write_date')) \ and ('date_trunc(\'second\', ' + x + ') as ' + x) \ - or '"' + x + '"' for x in fields_pre] + or '"' + x + '"' for x in fields_pre \ + if x != '_timestamp'] + if '_timestamp' in fields_pre: + fields_pre2 += ['(CASE WHEN write_date IS NOT NULL ' \ + 'THEN write_date ELSE create_date END) ' \ + 'AS _timestamp'] for i in range(0, len(ids), cursor.IN_MAX): sub_ids = ids[i:i + cursor.IN_MAX] if domain1: @@ -1051,6 +1057,8 @@ class ORM(object): res = [{'id': x} for x in ids] for field in fields_pre: + if field == '_timestamp': + continue if self._columns[field].translate: ids = [x['id'] for x in res] res_trans = self.pool.get('ir.translation')._get_ids(cursor, @@ -1297,26 +1305,35 @@ class ORM(object): ''' if context is None: context = {} + context = context.copy() if not ids: return True if isinstance(ids, (int, long)): ids = [ids] if self.table_query(context): return True - delta = context.get('read_delta', False) - if delta: + + if context.get('_timestamp', False): for i in range(0, len(ids), cursor.IN_MAX): sub_ids = ids[i:i + cursor.IN_MAX] - cursor.execute( - "SELECT (now() - min(write_date)) <= '%s'::interval " \ - "FROM \"%s\" WHERE id in (%s)" % \ - (delta, self._table, - ",".join(['%s' for x in sub_ids])), sub_ids) - res = cursor.fetchone() - if res and res[0]: - raise Exception('ConcurrencyException', - 'This record was modified in the meanwhile') - del context['read_delta'] + clause = '(id = %s AND ' \ + '(CASE WHEN write_date IS NOT NULL ' \ + 'THEN write_date ELSE create_date END) ' \ + ' > %s)' + args = [] + for i in sub_ids: + if context['_timestamp'].get(i): + args.append(i) + args.append(context['_timestamp'][i]) + if args: + cursor.execute("SELECT id " \ + 'FROM "' + self._table + '" ' \ + 'WHERE ' + ' OR '.join( + [clause for x in range(len(args)/2)]), args) + if cursor.rowcount: + raise Exception('ConcurrencyException', + 'Records were modified in the meanwhile') + del context['_timestamp'] self.pool.get('ir.model.access').check(cursor, user, self._name, 'delete') @@ -1423,6 +1440,7 @@ class ORM(object): ''' if context is None: context = {} + context = context.copy() if not ids: return True if self.table_query(context): @@ -1432,24 +1450,28 @@ class ORM(object): if isinstance(ids, (int, long)): ids = [ids] - delta = context.get('read_delta', False) - if delta: + + if context.get('_timestamp', False): for i in range(0, len(ids), cursor.IN_MAX): sub_ids = ids[i:i + cursor.IN_MAX] - cursor.execute("SELECT " \ - "(now() - min(write_date)) <= " \ - "'" + str(delta) + "'::interval " \ - 'FROM "' + self._table + '" ' \ - "WHERE id IN (" + ','.join('%s' for x in sub_ids) + ")", - sub_ids) - res = cursor.fetchone() - if res and res[0]: - for field in vals: - if field in self._columns \ - and self._columns[field]._classic_write: - raise Exception('ConcurrencyException', - 'This record was modified in the meanwhile') - del context['read_delta'] + clause = '(id = %s AND ' \ + '(CASE WHEN write_date IS NOT NULL ' \ + 'THEN write_date ELSE create_date END) ' \ + ' > %s)' + args = [] + for i in sub_ids: + if context['_timestamp'].get(i): + args.append(i) + args.append(context['_timestamp'][i]) + if args: + cursor.execute("SELECT id " \ + 'FROM "' + self._table + '" ' \ + 'WHERE ' + ' OR '.join( + [clause for x in range(len(args)/2)]), args) + if cursor.rowcount: + raise Exception('ConcurrencyException', + 'Records were modified in the meanwhile') + del context['_timestamp'] self.pool.get('ir.model.access').check(cursor, user, self._name, 'write')