Commit eecd273c authored by Philippe Pepiot's avatar Philippe Pepiot
Browse files

Avoid to update inlined relation column to NULL when deleting entities

When deleting entities, cubicweb run a rql DELETE on all relations to trigger hooks.
For an inlined relation this also mean set the column to NULL. This operation
may fail if there's additional constraints on the column.

Also this is a weird and useless behavior since deleting the entity row will by
definition delete the relation.

We still doesn't handle the case where both subject and object are going to be
deleted because rows need to be deleted in a particular order that cubicweb
doesn't handle.

Add a test checking UPDATE does not occur but hooks are correctly called.

Closes #17236690

--HG--
branch : 3.26
parent 0481ece35cb2
......@@ -653,6 +653,18 @@ class NativeSQLSource(SQLAdapterMixIn, AbstractSource):
def delete_relation(self, cnx, subject, rtype, object):
"""delete a relation from the source"""
rschema = self.schema.rschema(rtype)
# we should not issue UPDATE on inlined relations when subject is going
# to be deleted.
# Unless object is also going to be deleted and is not equal to the
# subject, in this case DELETE should occur in a order which cubicweb
# doesn't handle (yet). For example
# cubicweb/server/test/unittest_migractions.py::MigrationCommandsTC::test_add_drop_entity_type
# trigger such case.
if (
rschema.inlined and cnx.deleted_in_transaction(subject)
and (subject == object or not cnx.deleted_in_transaction(object))
):
return
self._delete_relation(cnx, subject, rtype, object, rschema.inlined)
if cnx.ertype_supports_undo(rtype):
self._record_tx_action(cnx, 'tx_relation_actions', u'R',
......
......@@ -322,6 +322,58 @@ class RepositoryTC(CubicWebTC):
cnx.commit()
self.assertEqual(bk.title, 'root')
def test_delete_entity_with_inlined_relation(self):
"""Test deletion of entity with inlined relations.
In this case, inlined relation column should not be deleted (= e.g. not
updated to NULL), but hooks before_delete_relation and
afer_delete_relation must be called.
"""
class OnDeleteInlined(Hook):
__regid__ = 'on_delete_inlined'
__select__ = Hook.__select__ & hook.match_rtype('personne_inlined')
events = ('before_delete_relation', 'after_delete_relation')
CALLED = []
def __call__(self):
OnDeleteInlined.CALLED.append(self.event)
with self.admin_access.cnx() as cnx:
# allow only one null on cw_personne_inlined column
cnx.system_sql(
'CREATE UNIQUE INDEX test_composite_idx ON cw_personne(true) '
'WHERE cw_personne_inlined is NULL')
cnx.commit()
# deletion of p1 should not set personne_inlined to NULL (otherwise
# the unique index will raise)
p0 = cnx.create_entity('Personne', nom=u'p0')
p1 = cnx.create_entity('Personne', nom=u'p1', personne_inlined=p0)
cnx.commit()
with self.temporary_appobjects(OnDeleteInlined):
cnx.entity_from_eid(p1.eid).cw_delete()
assert OnDeleteInlined.CALLED == [
'before_delete_relation', 'after_delete_relation']
cnx.commit()
# XXX: This case is not handled because entities need to be deleted
# in a specific order
# p1 = cnx.create_entity('Personne', nom=u'p1', personne_inlined=p0)
# cnx.commit()
# cnx.execute('DELETE Personne X WHERE X eid IN ({}, {})'.format(p1.eid, p0.eid))
# cnx.commit()
cnx.entity_from_eid(p0.eid).cw_delete()
cnx.commit()
# deletion of p1 should not set personne_inlined to NULL
p1 = cnx.create_entity('Personne', nom=u'p1')
p1.cw_set(personne_inlined=p1)
p0 = cnx.create_entity('Personne', nom=u'p0')
cnx.commit()
cnx.entity_from_eid(p1.eid).cw_delete()
cnx.commit()
class SchemaDeserialTC(CubicWebTC):
appid = 'data-schemaserial'
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment