diff --git a/addons/hr_holidays/tests/test_company_leave.py b/addons/hr_holidays/tests/test_company_leave.py
index 486c353425c..2fa68336e4e 100644
--- a/addons/hr_holidays/tests/test_company_leave.py
+++ b/addons/hr_holidays/tests/test_company_leave.py
@@ -313,7 +313,7 @@ class TestCompanyLeave(TransactionCase):
})
company_leave._compute_date_from_to()
- with self.assertQueryCount(__system__=735, admin=865):
+ with self.assertQueryCount(__system__=736, admin=865):
# Original query count: 1987
# Without tracking/activity context keys: 5154
company_leave.action_validate()
diff --git a/addons/test_mail/tests/test_performance.py b/addons/test_mail/tests/test_performance.py
index ecc2b110715..20788ed23c7 100644
--- a/addons/test_mail/tests/test_performance.py
+++ b/addons/test_mail/tests/test_performance.py
@@ -231,7 +231,7 @@ class TestMailAPIPerformance(BaseMailPerformance):
#voip module read activity_type during create leading to one less query in enterprise on action_feedback
category = activity.activity_type_id.category
- with self.assertQueryCount(__system__=16, emp=20):
+ with self.assertQueryCount(__system__=16, emp=18):
activity.action_feedback(feedback='Zizisse Done !')
@users('__system__', 'emp')
@@ -248,7 +248,7 @@ class TestMailAPIPerformance(BaseMailPerformance):
record.write({'name': 'Dupe write'})
- with self.assertQueryCount(__system__=17, emp=20):
+ with self.assertQueryCount(__system__=17, emp=19):
record.action_close('Dupe feedback')
self.assertEqual(record.activity_ids, self.env['mail.activity'])
@@ -270,7 +270,7 @@ class TestMailAPIPerformance(BaseMailPerformance):
'partner_ids': [(4, customer_id)],
})
- with self.assertQueryCount(__system__=25, emp=32):
+ with self.assertQueryCount(__system__=24, emp=31):
composer.send_mail()
@users('__system__', 'emp')
@@ -281,7 +281,7 @@ class TestMailAPIPerformance(BaseMailPerformance):
test_record = self.env['mail.test.ticket'].browse(self.test_record_full.id)
test_template = self.env['mail.template'].browse(self.test_template_full.id)
# TODO FIXME non deterministic, last check 25 Mar 2020. Runbot at most +7 compared to local.
- with self.assertQueryCount(__system__=11, emp=13):
+ with self.assertQueryCount(__system__=11, emp=12):
composer = self.env['mail.compose.message'].with_context({
'default_composition_mode': 'comment',
'default_model': test_record._name,
@@ -290,7 +290,7 @@ class TestMailAPIPerformance(BaseMailPerformance):
}).create({})
composer.onchange_template_id_wrapper()
- with self.assertQueryCount(__system__=33, emp=40):
+ with self.assertQueryCount(__system__=32, emp=39):
composer.send_mail()
# remove created partner to ensure tests are the same each run
@@ -302,7 +302,7 @@ class TestMailAPIPerformance(BaseMailPerformance):
def test_message_assignation_email(self):
self.user_test.write({'notification_type': 'email'})
record = self.env['mail.test.track'].create({'name': 'Test'})
- with self.assertQueryCount(__system__=37, emp=38):
+ with self.assertQueryCount(__system__=36, emp=37):
record.write({
'user_id': self.user_test.id,
})
@@ -355,7 +355,7 @@ class TestMailAPIPerformance(BaseMailPerformance):
def test_message_post_one_email_notification(self):
record = self.env['mail.test.simple'].create({'name': 'Test'})
- with self.assertQueryCount(__system__=28, emp=31):
+ with self.assertQueryCount(__system__=27, emp=30):
record.message_post(
body='
Test Post Performances with an email ping
',
partner_ids=self.customer.ids,
@@ -481,7 +481,7 @@ class TestMailComplexPerformance(BaseMailPerformance):
'recipient_ids': [(4, pid) for pid in self.partners.ids],
})
mail_ids = mail.ids
- with self.assertQueryCount(__system__=7, emp=7):
+ with self.assertQueryCount(__system__=6, emp=6):
self.env['mail.mail'].sudo().browse(mail_ids).send()
self.assertEqual(mail.body_html, 'Test
')
@@ -494,7 +494,7 @@ class TestMailComplexPerformance(BaseMailPerformance):
self.container.message_subscribe(self.user_portal.partner_id.ids)
record = self.container.with_user(self.env.user)
- with self.assertQueryCount(__system__=63, emp=64):
+ with self.assertQueryCount(__system__=61, emp=62):
record.message_post(
body='Test Post Performances
',
message_type='comment',
@@ -511,7 +511,7 @@ class TestMailComplexPerformance(BaseMailPerformance):
record = self.container.with_user(self.env.user)
template_id = self.env.ref('test_mail.mail_test_container_tpl').id
- with self.assertQueryCount(__system__=73, emp=75):
+ with self.assertQueryCount(__system__=70, emp=71):
record.message_post_with_template(template_id, message_type='comment', composition_mode='comment')
self.assertEqual(record.message_ids[0].body, 'Adding stuff on %s
' % record.name)
@@ -582,7 +582,7 @@ class TestMailComplexPerformance(BaseMailPerformance):
})
rec1 = rec.with_context(active_test=False) # to see inactive records
self.assertEqual(rec1.message_partner_ids, self.partners | self.env.user.partner_id)
- with self.assertQueryCount(__system__=37, emp=38):
+ with self.assertQueryCount(__system__=36, emp=37):
rec.write({'user_id': self.user_portal.id})
self.assertEqual(rec1.message_partner_ids, self.partners | self.env.user.partner_id | self.user_portal.partner_id)
# write tracking message
@@ -602,7 +602,7 @@ class TestMailComplexPerformance(BaseMailPerformance):
customer_id = self.customer.id
user_id = self.user_portal.id
- with self.assertQueryCount(__system__=108, emp=109):
+ with self.assertQueryCount(__system__=105, emp=106):
rec = self.env['mail.test.ticket'].create({
'name': 'Test',
'container_id': container_id,
@@ -631,7 +631,7 @@ class TestMailComplexPerformance(BaseMailPerformance):
rec1 = rec.with_context(active_test=False) # to see inactive records
self.assertEqual(rec1.message_partner_ids, self.user_portal.partner_id | self.env.user.partner_id)
self.assertEqual(len(rec1.message_ids), 1)
- with self.assertQueryCount(__system__=77, emp=77):
+ with self.assertQueryCount(__system__=75, emp=75):
rec.write({
'name': 'Test2',
'container_id': self.container.id,
@@ -668,7 +668,7 @@ class TestMailComplexPerformance(BaseMailPerformance):
rec1 = rec.with_context(active_test=False) # to see inactive records
self.assertEqual(rec1.message_partner_ids, self.user_portal.partner_id | self.env.user.partner_id)
- with self.assertQueryCount(__system__=85, emp=85):
+ with self.assertQueryCount(__system__=83, emp=83):
rec.write({
'name': 'Test2',
'container_id': container_id,
@@ -701,7 +701,7 @@ class TestMailComplexPerformance(BaseMailPerformance):
rec1 = rec.with_context(active_test=False) # to see inactive records
self.assertEqual(rec1.message_partner_ids, self.partners | self.env.user.partner_id | self.user_portal.partner_id)
- with self.assertQueryCount(__system__=29, emp=30):
+ with self.assertQueryCount(__system__=28, emp=29):
rec.write({
'name': 'Test2',
'customer_id': customer_id,
@@ -951,7 +951,7 @@ class TestMailHeavyPerformancePost(BaseMailPerformance):
]
self.attachements = self.env['ir.attachment'].with_user(self.env.user).create(self.vals)
attachement_ids = self.attachements.ids
- with self.assertQueryCount(emp=79):
+ with self.assertQueryCount(emp=76):
self.cr.sql_log = self.warm and self.cr.sql_log_count
record.with_context({}).message_post(
body='Test body

',
diff --git a/addons/test_mass_mailing/tests/test_performance.py b/addons/test_mass_mailing/tests/test_performance.py
index 85fdf26bae8..127857c4d4f 100644
--- a/addons/test_mass_mailing/tests/test_performance.py
+++ b/addons/test_mass_mailing/tests/test_performance.py
@@ -51,7 +51,7 @@ class TestMassMailPerformance(TestMassMailPerformanceBase):
})
# runbot needs +50 compared to local
- with self.assertQueryCount(__system__=1714, marketing=1715):
+ with self.assertQueryCount(__system__=1664, marketing=1664):
mailing.action_send_mail()
self.assertEqual(mailing.sent, 50)
@@ -91,7 +91,7 @@ class TestMassMailBlPerformance(TestMassMailPerformanceBase):
})
# runbot needs +62 compared to local
- with self.assertQueryCount(__system__=1992, marketing=1993):
+ with self.assertQueryCount(__system__=1942, marketing=1942):
mailing.action_send_mail()
self.assertEqual(mailing.sent, 50)
diff --git a/odoo/addons/test_assetsbundle/tests/test_assetsbundle.py b/odoo/addons/test_assetsbundle/tests/test_assetsbundle.py
index 2ac03de2f8e..5cd488c0c10 100644
--- a/odoo/addons/test_assetsbundle/tests/test_assetsbundle.py
+++ b/odoo/addons/test_assetsbundle/tests/test_assetsbundle.py
@@ -706,8 +706,9 @@ class TestAssetsBundleWithIRAMock(FileTouchable):
# has really been modified. If we do not update the write_date to a posterior date, we are
# not able to reproduce the case where we compile this bundle again without changing
# anything.
- self.env['ir.attachment'].flush(['checksum'])
+ self.env['ir.attachment'].flush(['checksum', 'write_date'])
self.cr.execute("update ir_attachment set write_date=clock_timestamp() + interval '10 seconds' where id = (select max(id) from ir_attachment)")
+ self.env['ir.attachment'].invalidate_cache(['write_date'])
# Compile a fourth time, without changes
self._bundle(self._get_asset(), False, False)
diff --git a/odoo/addons/test_new_api/tests/test_new_fields.py b/odoo/addons/test_new_api/tests/test_new_fields.py
index c4c04982688..0195277d990 100644
--- a/odoo/addons/test_new_api/tests/test_new_fields.py
+++ b/odoo/addons/test_new_api/tests/test_new_fields.py
@@ -3115,7 +3115,7 @@ class TestFieldParametersValidation(common.TransactionCase):
def insert(model, *fnames):
""" Return the expected query string to INSERT the given columns. """
- columns = ['create_uid', 'create_date', 'write_uid', 'write_date'] + sorted(fnames)
+ columns = sorted(fnames + ('create_uid', 'create_date', 'write_uid', 'write_date'))
return 'INSERT INTO "{}" ("id", {}) VALUES (nextval(%s), {}) RETURNING id'.format(
model._table,
", ".join('"{}"'.format(column) for column in columns),
@@ -3125,7 +3125,7 @@ def insert(model, *fnames):
def update(model, *fnames):
""" Return the expected query string to UPDATE the given columns. """
- columns = sorted(fnames) + ['write_uid', 'write_date']
+ columns = sorted(fnames + ('write_uid', 'write_date'))
return 'UPDATE "{}" SET {} WHERE id IN %s'.format(
model._table,
", ".join('"{}" = %s'.format(column) for column in columns),
diff --git a/odoo/addons/test_performance/tests/test_performance.py b/odoo/addons/test_performance/tests/test_performance.py
index d95f51c7d8b..04f5c089f5b 100644
--- a/odoo/addons/test_performance/tests/test_performance.py
+++ b/odoo/addons/test_performance/tests/test_performance.py
@@ -136,12 +136,12 @@ class TestPerformance(SavepointCaseWithUserDemo):
self.assertEqual(rec1.line_ids, lines)
# delete N lines: O(1) queries
- with self.assertQueryCount(14):
+ with self.assertQueryCount(15):
rec1.invalidate_cache()
rec1.write({'line_ids': [Command.delete(line.id) for line in lines[0]]})
self.assertEqual(rec1.line_ids, lines[1:])
- with self.assertQueryCount(12):
+ with self.assertQueryCount(13):
rec1.invalidate_cache()
rec1.write({'line_ids': [Command.delete(line.id) for line in lines[1:]]})
self.assertFalse(rec1.line_ids)
@@ -151,12 +151,12 @@ class TestPerformance(SavepointCaseWithUserDemo):
lines = rec1.line_ids
# unlink N lines: O(1) queries
- with self.assertQueryCount(14):
+ with self.assertQueryCount(15):
rec1.invalidate_cache()
rec1.write({'line_ids': [Command.unlink(line.id) for line in lines[0]]})
self.assertEqual(rec1.line_ids, lines[1:])
- with self.assertQueryCount(12):
+ with self.assertQueryCount(13):
rec1.invalidate_cache()
rec1.write({'line_ids': [Command.unlink(line.id) for line in lines[1:]]})
self.assertFalse(rec1.line_ids)
@@ -190,7 +190,7 @@ class TestPerformance(SavepointCaseWithUserDemo):
self.assertEqual(rec2.line_ids, lines)
# empty N lines in rec2: O(1) queries
- with self.assertQueryCount(__system__=13, demo=13):
+ with self.assertQueryCount(14):
rec1.invalidate_cache()
rec2.write({'line_ids': [Command.clear()]})
self.assertFalse(rec2.line_ids)
@@ -210,13 +210,13 @@ class TestPerformance(SavepointCaseWithUserDemo):
self.assertEqual(rec1.line_ids, lines[1:])
self.assertEqual(rec2.line_ids, lines[0])
- with self.assertQueryCount(7):
+ with self.assertQueryCount(6):
rec1.invalidate_cache()
rec2.write({'line_ids': [Command.set(lines.ids)]})
self.assertFalse(rec1.line_ids)
self.assertEqual(rec2.line_ids, lines)
- with self.assertQueryCount(5):
+ with self.assertQueryCount(4):
rec1.invalidate_cache()
rec2.write({'line_ids': [Command.set(lines.ids)]})
self.assertEqual(rec2.line_ids, lines)
diff --git a/odoo/models.py b/odoo/models.py
index 96215434e2b..55f28cf11fc 100644
--- a/odoo/models.py
+++ b/odoo/models.py
@@ -3582,6 +3582,12 @@ Fields:
if not(self.env.uid == SUPERUSER_ID and not self.pool.ready):
bad_names.update(LOG_ACCESS_COLUMNS)
+ # set magic fields
+ vals = {key: val for key, val in vals.items() if key not in bad_names}
+ if self._log_access:
+ vals.setdefault('write_uid', self.env.uid)
+ vals.setdefault('write_date', self.env.cr.now())
+
determine_inverses = defaultdict(list) # {inverse: fields}
records_to_inverse = {} # {field: records}
relational_names = []
@@ -3645,24 +3651,9 @@ Fields:
real_recs = self.filtered('id')
- # If there are only fields that do not trigger _write (e.g. only
- # determine inverse), the below ensures that `write_date` and
- # `write_uid` are updated (`test_orm.py`, `test_write_date`)
- if self._log_access and self.ids:
- towrite = env.all.towrite[self._name]
- for record in real_recs:
- towrite[record.id]['write_uid'] = self.env.uid
- towrite[record.id]['write_date'] = False
- self.env.cache.invalidate([
- (self._fields['write_date'], self.ids),
- (self._fields['write_uid'], self.ids),
- ])
-
# for monetary field, their related currency field must be cached
# before the amount so it can be rounded correctly
for fname in sorted(vals, key=lambda x: self._fields[x].type=='monetary'):
- if fname in bad_names:
- continue
field = self._fields[fname]
field.write(self, vals[fname])
@@ -3722,6 +3713,12 @@ Fields:
# determine records that require updating parent_path
parent_records = self._parent_store_update_prepare(vals)
+ if self._log_access:
+ # set magic fields (already done by write(), but not for computed fields)
+ vals = dict(vals)
+ vals.setdefault('write_uid', self.env.uid)
+ vals.setdefault('write_date', self.env.cr.now())
+
# determine SQL values
columns = [] # list of (column_name, format, value)
@@ -3737,12 +3734,6 @@ Fields:
assert field.column_type
columns.append((name, field.column_format, val))
- if self._log_access:
- if not vals.get('write_uid'):
- columns.append(('write_uid', '%s', self._uid))
- if not vals.get('write_date'):
- columns.append(('write_date', '%s', AsIs("(now() at time zone 'UTC')")))
-
# update columns
if columns:
query = 'UPDATE "%s" SET %s WHERE id IN %%s' % (
@@ -3810,6 +3801,15 @@ Fields:
# add missing defaults
vals = self._add_missing_default_values(vals)
+ # set magic fields
+ for name in bad_names:
+ vals.pop(name, None)
+ if self._log_access:
+ vals.setdefault('create_uid', self.env.uid)
+ vals.setdefault('create_date', self.env.cr.now())
+ vals.setdefault('write_uid', self.env.uid)
+ vals.setdefault('write_date', self.env.cr.now())
+
# distribute fields into sets for various purposes
data = {}
data['stored'] = stored = {}
@@ -3817,8 +3817,6 @@ Fields:
data['inherited'] = inherited = defaultdict(dict)
data['protected'] = protected = set()
for key, val in vals.items():
- if key in bad_names:
- continue
field = self._fields.get(key)
if not field:
raise ValueError("Invalid field %r on model %r" % (key, self._name))
@@ -3919,18 +3917,10 @@ Fields:
other_fields = set() # non-column fields
translated_fields = set() # translated fields
- # column names, formats and values (for common fields)
- columns0 = [('id', "nextval(%s)", self._sequence)]
- if self._log_access:
- columns0.append(('create_uid', "%s", self._uid))
- columns0.append(('create_date', "%s", AsIs("(now() at time zone 'UTC')")))
- columns0.append(('write_uid', "%s", self._uid))
- columns0.append(('write_date', "%s", AsIs("(now() at time zone 'UTC')")))
-
for data in data_list:
# determine column values
stored = data['stored']
- columns = [column for column in columns0 if column[0] not in stored]
+ columns = [('id', "nextval(%s)", self._sequence)]
for name, val in sorted(stored.items()):
field = self._fields[name]
assert field.store
diff --git a/odoo/sql_db.py b/odoo/sql_db.py
index 7df9f6011fb..92aa9bcf56a 100644
--- a/odoo/sql_db.py
+++ b/odoo/sql_db.py
@@ -257,6 +257,7 @@ class Cursor(BaseCursor):
self._default_log_exceptions = True
self.cache = {}
+ self._now = None
def __build_dict(self, row):
return {d.name: row[i] for i, d in enumerate(self._obj.description)}
@@ -444,6 +445,7 @@ class Cursor(BaseCursor):
flush_env(self)
self.precommit.run()
result = self._cnx.commit()
+ self._now = None
self.prerollback.clear()
self.postrollback.clear()
self.postcommit.run()
@@ -457,6 +459,7 @@ class Cursor(BaseCursor):
self.postcommit.clear()
self.prerollback.run()
result = self._cnx.rollback()
+ self._now = None
self.postrollback.run()
return result
@@ -468,6 +471,13 @@ class Cursor(BaseCursor):
def closed(self):
return self._closed
+ def now(self):
+ """ Return the transaction's timestamp ``NOW() AT TIME ZONE 'UTC'``. """
+ if self._now is None:
+ self.execute("SELECT (now() AT TIME ZONE 'UTC')")
+ self._now = self.fetchone()[0]
+ return self._now
+
class TestCursor(BaseCursor):
""" A pseudo-cursor to be used for tests, on top of a real cursor. It keeps