# HG changeset patch # User Sylvain Thénault # Date 1268129129 -3600 # Node ID dc351b96f596d9b15823261b4dda482259fa7869 # Parent ad78b118b1240b70d0caf237191db0c4c8279637# Parent 3653e09024a1db44ed9f7dcf1f43152319417b34 backport stable diff -r ad78b118b124 -r dc351b96f596 entities/schemaobjs.py --- a/entities/schemaobjs.py Tue Mar 09 11:01:44 2010 +0100 +++ b/entities/schemaobjs.py Tue Mar 09 11:05:29 2010 +0100 @@ -56,30 +56,21 @@ return u'%s <<%s>>' % (self.dc_title(), ', '.join(stereotypes)) return self.dc_title() - def inlined_changed(self, inlined): - """check inlining is necessary and possible: - - * return False if nothing has changed - * raise ValidationError if inlining is'nt possible - * eventually return True + def check_inlined_allowed(self): + """check inlining is possible, raise ValidationError if not possible """ - rschema = self._cw.vreg.schema.rschema(self.name) - if inlined == rschema.inlined: - return False - if inlined: - # don't use the persistent schema, we may miss cardinality changes - # in the same transaction - for rdef in self.reverse_relation_type: - card = rdef.cardinality[0] - if not card in '?1': - rtype = self.name - stype = rdef.stype - otype = rdef.otype - msg = self._cw._("can't set inlined=%(inlined)s, " - "%(stype)s %(rtype)s %(otype)s " - "has cardinality=%(card)s") - raise ValidationError(self.eid, {'inlined': msg % locals()}) - return True + # don't use the persistent schema, we may miss cardinality changes + # in the same transaction + for rdef in self.reverse_relation_type: + card = rdef.cardinality[0] + if not card in '?1': + rtype = self.name + stype = rdef.stype + otype = rdef.otype + msg = self._cw._("can't set inlined=%(inlined)s, " + "%(stype)s %(rtype)s %(otype)s " + "has cardinality=%(card)s") + raise ValidationError(self.eid, {'inlined': msg % locals()}) def db_key_name(self): """XXX goa specific""" diff -r ad78b118b124 -r dc351b96f596 goa/goactl.py --- a/goa/goactl.py Tue Mar 09 11:01:44 2010 +0100 +++ b/goa/goactl.py Tue Mar 09 11:05:29 2010 +0100 @@ -15,38 +15,40 @@ create_dir) from cubicweb.cwconfig import CubicWebConfiguration -from logilab import common as lgc -from logilab import constraint as lgcstr -from logilab import mtconverter as lgmtc -import rql, yams, yapps, simplejson, docutils, roman -SLINK_DIRECTORIES = [ - (lgc.__path__[0], 'logilab/common'), - (lgmtc.__path__[0], 'logilab/mtconverter'), - (lgcstr.__path__[0], 'logilab/constraint'), - (rql.__path__[0], 'rql'), - (simplejson.__path__[0], 'simplejson'), - (yams.__path__[0], 'yams'), - (yapps.__path__[0], 'yapps'), - (docutils.__path__[0], 'docutils'), - (roman.__file__.replace('.pyc', '.py'), 'roman.py'), +def slink_directories(): + import rql, yams, yapps, simplejson, docutils, roman + from logilab import common as lgc + from logilab import constraint as lgcstr + from logilab import mtconverter as lgmtc + dirs = [ + (lgc.__path__[0], 'logilab/common'), + (lgmtc.__path__[0], 'logilab/mtconverter'), + (lgcstr.__path__[0], 'logilab/constraint'), + (rql.__path__[0], 'rql'), + (simplejson.__path__[0], 'simplejson'), + (yams.__path__[0], 'yams'), + (yapps.__path__[0], 'yapps'), + (docutils.__path__[0], 'docutils'), + (roman.__file__.replace('.pyc', '.py'), 'roman.py'), - ('/usr/share/fckeditor/', 'fckeditor'), + ('/usr/share/fckeditor/', 'fckeditor'), + + (join(CW_SOFTWARE_ROOT, 'web', 'data'), join('cubes', 'shared', 'data')), + (join(CW_SOFTWARE_ROOT, 'web', 'wdoc'), join('cubes', 'shared', 'wdoc')), + (join(CW_SOFTWARE_ROOT, 'i18n'), join('cubes', 'shared', 'i18n')), + (join(CW_SOFTWARE_ROOT, 'goa', 'tools'), 'tools'), + (join(CW_SOFTWARE_ROOT, 'goa', 'bin'), 'bin'), + ] - (join(CW_SOFTWARE_ROOT, 'web', 'data'), join('cubes', 'shared', 'data')), - (join(CW_SOFTWARE_ROOT, 'web', 'wdoc'), join('cubes', 'shared', 'wdoc')), - (join(CW_SOFTWARE_ROOT, 'i18n'), join('cubes', 'shared', 'i18n')), - (join(CW_SOFTWARE_ROOT, 'goa', 'tools'), 'tools'), - (join(CW_SOFTWARE_ROOT, 'goa', 'bin'), 'bin'), - ] - -try: - import dateutil - import vobject - SLINK_DIRECTORIES.extend([ (dateutil.__path__[0], 'dateutil'), - (vobject.__path__[0], 'vobject') ] ) -except ImportError: - pass + try: + import dateutil + import vobject + dirs.extend([ (dateutil.__path__[0], 'dateutil'), + (vobject.__path__[0], 'vobject') ] ) + except ImportError: + pass + return dirs COPY_CW_FILES = ( '__init__.py', @@ -194,7 +196,7 @@ copy_skeleton(join(CW_SOFTWARE_ROOT, 'goa', 'skel'), appldir, context, askconfirm=True) # cubicweb core dependancies - for directory, subdirectory in SLINK_DIRECTORIES: + for directory, subdirectory in slink_directories(): subdirectory = join(appldir, subdirectory) if not exists(split(subdirectory)[0]): create_dir(split(subdirectory)[0]) diff -r ad78b118b124 -r dc351b96f596 hooks/syncschema.py --- a/hooks/syncschema.py Tue Mar 09 11:01:44 2010 +0100 +++ b/hooks/syncschema.py Tue Mar 09 11:05:29 2010 +0100 @@ -227,25 +227,26 @@ class SourceDbCWRTypeUpdate(hook.Operation): """actually update some properties of a relation definition""" - rschema = entity = None # make pylint happy + rschema = entity = values = None # make pylint happy def precommit_event(self): + rschema = self.rschema + if rschema.final: + return session = self.session - rschema = self.rschema - entity = self.entity - if 'fulltext_container' in entity.edited_attributes: + if 'fulltext_container' in self.values: ftiupdates = session.transaction_data.setdefault( 'fti_update_etypes', set()) for subjtype, objtype in rschema.rdefs: ftiupdates.add(subjtype) ftiupdates.add(objtype) UpdateFTIndexOp(session) - if rschema.final or not 'inlined' in entity.edited_attributes: + if not 'inlined' in self.values: return # nothing to do - inlined = entity.inlined + inlined = self.values['inlined'] # check in-lining is necessary / possible - if not entity.inlined_changed(inlined): - return # nothing to do + if inlined: + self.entity.check_inlined_allowed() # inlined changed, make necessary physical changes! sqlexec = self.session.system_sql rtype = rschema.type @@ -934,26 +935,22 @@ class BeforeUpdateCWRTypeHook(DelCWRTypeHook): """check name change, handle final""" - __regid__ = 'checkupdatecwrtype' + __regid__ = 'syncupdatecwrtype' events = ('before_update_entity',) def __call__(self): - check_valid_changes(self._cw, self.entity) - - -class AfterUpdateCWRTypeHook(DelCWRTypeHook): - __regid__ = 'syncupdatecwrtype' - events = ('after_update_entity',) - - def __call__(self): entity = self.entity + check_valid_changes(self._cw, entity) newvalues = {} for prop in ('symmetric', 'inlined', 'fulltext_container'): if prop in entity.edited_attributes: - newvalues[prop] = entity[prop] + old, new = hook.entity_oldnewvalue(entity, prop) + if old != new: + newvalues[prop] = entity[prop] if newvalues: rschema = self._cw.vreg.schema.rschema(entity.name) - SourceDbCWRTypeUpdate(self._cw, rschema=rschema, entity=entity) + SourceDbCWRTypeUpdate(self._cw, rschema=rschema, entity=entity, + values=newvalues) MemSchemaCWRTypeUpdate(self._cw, rschema=rschema, values=newvalues) @@ -1033,8 +1030,8 @@ class AfterUpdateCWRDefHook(SyncSchemaHook): __regid__ = 'syncaddcwattribute' __select__ = SyncSchemaHook.__select__ & implements('CWAttribute', - 'CWRelation') - events = ('after_update_entity',) + 'CWRelation') + events = ('before_update_entity',) def __call__(self): entity = self.entity @@ -1049,7 +1046,9 @@ if prop == 'order': prop = 'ordernum' if prop in entity.edited_attributes: - newvalues[prop] = entity[prop] + old, new = hook.entity_oldnewvalue(entity, prop) + if old != new: + newvalues[prop] = entity[prop] if newvalues: subjtype = entity.stype.name MemSchemaRDefUpdate(self._cw, kobj=(subjtype, desttype), diff -r ad78b118b124 -r dc351b96f596 hooks/test/unittest_syncschema.py --- a/hooks/test/unittest_syncschema.py Tue Mar 09 11:01:44 2010 +0100 +++ b/hooks/test/unittest_syncschema.py Tue Mar 09 11:05:29 2010 +0100 @@ -266,7 +266,7 @@ 'WHERE E is CWEType, E name "Email", A is CWAttribute,' 'A from_entity E, A relation_type R, R name "subject"') self.commit() - rset = req.execute('Any X Where X has_text "rick.roll"') + rset = req.execute('Any X WHERE X has_text "rick.roll"') self.failIf(rset) assert req.execute('SET A fulltextindexed TRUE ' 'WHERE A from_entity E, A relation_type R, ' @@ -285,7 +285,7 @@ assert self.execute('SET R fulltext_container NULL ' 'WHERE R name "use_email"') self.commit() - rset = self.execute('Any X Where X has_text "rick.roll"') + rset = self.execute('Any X WHERE X has_text "rick.roll"') self.assertIn(target.eid, [item[0] for item in rset]) assert self.execute('SET R fulltext_container "subject" ' 'WHERE R name "use_email"') diff -r ad78b118b124 -r dc351b96f596 server/__init__.py diff -r ad78b118b124 -r dc351b96f596 server/hook.py --- a/server/hook.py Tue Mar 09 11:01:44 2010 +0100 +++ b/server/hook.py Tue Mar 09 11:05:29 2010 +0100 @@ -95,16 +95,16 @@ VRegistry.REGISTRY_FACTORY['hooks'] = HooksRegistry - +_MARKER = object() def entity_oldnewvalue(entity, attr): """returns the couple (old attr value, new attr value) NOTE: will only work in a before_update_entity hook """ # get new value and remove from local dict to force a db query to # fetch old value - newvalue = entity.pop(attr, None) + newvalue = entity.pop(attr, _MARKER) oldvalue = getattr(entity, attr) - if newvalue is not None: + if newvalue is not _MARKER: entity[attr] = newvalue return oldvalue, newvalue diff -r ad78b118b124 -r dc351b96f596 server/hookhelper.py --- a/server/hookhelper.py Tue Mar 09 11:01:44 2010 +0100 +++ b/server/hookhelper.py Tue Mar 09 11:05:29 2010 +0100 @@ -13,7 +13,6 @@ @deprecated('[3.6] entity_oldnewvalue should be imported from cw.server.hook') def entity_oldnewvalue(entity, attr): - """return the "name" attribute of the entity with the given eid""" return hook.entity_oldnewvalue(entity, attr) @deprecated('[3.6] entity_name is deprecated, use entity.name') diff -r ad78b118b124 -r dc351b96f596 server/serverctl.py --- a/server/serverctl.py Tue Mar 09 11:01:44 2010 +0100 +++ b/server/serverctl.py Tue Mar 09 11:05:29 2010 +0100 @@ -66,7 +66,13 @@ cnx = get_connection(driver, dbhost, dbname, user, password=password, port=source.get('db-port'), **extra) - cnx.logged_user = logged_user + try: + cnx.logged_user = user + except AttributeError: + # C object, __slots__ + from logilab.db import _SimpleConnectionWrapper + cnx = _SimpleConnectionWrapper(cnx) + cnx.logged_user = user return cnx def system_source_cnx(source, dbms_system_base=False, diff -r ad78b118b124 -r dc351b96f596 server/session.py --- a/server/session.py Tue Mar 09 11:01:44 2010 +0100 +++ b/server/session.py Tue Mar 09 11:05:29 2010 +0100 @@ -280,9 +280,15 @@ self.set_language(value) def deleted_in_transaction(self, eid): + """return True if the entity of the given eid is being deleted in the + current transaction + """ return eid in self.transaction_data.get('pendingeids', ()) def added_in_transaction(self, eid): + """return True if the entity of the given eid is being created in the + current transaction + """ return eid in self.transaction_data.get('neweids', ()) def schema_rproperty(self, rtype, eidfrom, eidto, rprop): diff -r ad78b118b124 -r dc351b96f596 server/sources/native.py diff -r ad78b118b124 -r dc351b96f596 server/sources/rql2sql.py --- a/server/sources/rql2sql.py Tue Mar 09 11:01:44 2010 +0100 +++ b/server/sources/rql2sql.py Tue Mar 09 11:05:29 2010 +0100 @@ -341,6 +341,9 @@ } if not self.dbms_helper.union_parentheses_support: self.union_sql = self.noparen_union_sql + if self.dbms_helper.fti_need_distinct: + self.__union_sql = self.union_sql + self.union_sql = self.has_text_need_distinct_union_sql self._lock = threading.Lock() if attrmap is None: attrmap = {} @@ -374,6 +377,12 @@ finally: self._lock.release() + def has_text_need_distinct_union_sql(self, union, needalias=False): + if getattr(union, 'has_text_query', False): + for select in union.children: + select.need_distinct = True + return self.__union_sql(union, needalias) + def union_sql(self, union, needalias=False): # pylint: disable-msg=E0202 if len(union.children) == 1: return self.select_sql(union.children[0], needalias) diff -r ad78b118b124 -r dc351b96f596 server/test/unittest_rql2sql.py --- a/server/test/unittest_rql2sql.py Tue Mar 09 11:01:44 2010 +0100 +++ b/server/test/unittest_rql2sql.py Tue Mar 09 11:05:29 2010 +0100 @@ -1486,26 +1486,26 @@ def test_has_text(self): for t in self._parse(( ('Any X WHERE X has_text "toto tata"', - """SELECT appears0.uid + """SELECT DISTINCT appears0.uid FROM appears AS appears0 WHERE appears0.word_id IN (SELECT word_id FROM word WHERE word in ('toto', 'tata'))"""), ('Any X WHERE X has_text %(text)s', - """SELECT appears0.uid + """SELECT DISTINCT appears0.uid FROM appears AS appears0 WHERE appears0.word_id IN (SELECT word_id FROM word WHERE word in ('hip', 'hop', 'momo'))"""), ('Personne X WHERE X has_text "toto tata"', - """SELECT _X.eid + """SELECT DISTINCT _X.eid FROM appears AS appears0, entities AS _X WHERE appears0.word_id IN (SELECT word_id FROM word WHERE word in ('toto', 'tata')) AND appears0.uid=_X.eid AND _X.type='Personne'"""), ('Any X WHERE X has_text "toto tata", X name "tutu", X is IN (Basket,Folder)', - """SELECT _X.cw_eid + """SELECT DISTINCT _X.cw_eid FROM appears AS appears0, cw_Basket AS _X WHERE appears0.word_id IN (SELECT word_id FROM word WHERE word in ('toto', 'tata')) AND appears0.uid=_X.cw_eid AND _X.cw_name=tutu -UNION ALL -SELECT _X.cw_eid +UNION +SELECT DISTINCT _X.cw_eid FROM appears AS appears0, cw_Folder AS _X WHERE appears0.word_id IN (SELECT word_id FROM word WHERE word in ('toto', 'tata')) AND appears0.uid=_X.cw_eid AND _X.cw_name=tutu """), diff -r ad78b118b124 -r dc351b96f596 sobjects/test/unittest_supervising.py --- a/sobjects/test/unittest_supervising.py Tue Mar 09 11:01:44 2010 +0100 +++ b/sobjects/test/unittest_supervising.py Tue Mar 09 11:05:29 2010 +0100 @@ -27,7 +27,6 @@ def test_supervision(self): - session = self.session # do some modification user = self.execute('INSERT CWUser X: X login "toto", X upassword "sosafe", X in_group G ' 'WHERE G name "users"').get_entity(0, 0) @@ -37,6 +36,7 @@ self.execute('SET X content "duh?" WHERE X is Comment') self.execute('DELETE X comments Y WHERE Y is Card, Y title "une autre news !"') # check only one supervision email operation + session = self.session sentops = [op for op in session.pending_operations if isinstance(op, SupervisionMailOp)] self.assertEquals(len(sentops), 1)