fix #343630 by getting explicitly TextInput widget for String, also instantiate field to get proper widget configuration
"""provide an abstract class for external sources using a sqlite database helper:organization: Logilab:copyright: 2007-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses"""__docformat__="restructuredtext en"fromos.pathimportjoin,existsfromcubicwebimportserverfromcubicweb.server.sqlutilsimportSQL_PREFIX,sqlexec,SQLAdapterMixInfromcubicweb.server.sourcesimportAbstractSource,nativefromcubicweb.server.sources.rql2sqlimportSQLGeneratorclassConnectionWrapper(object):def__init__(self,source=None):self.source=sourceself._cnx=None@propertydeflogged_user(self):ifself._cnxisNone:self._cnx=self.source._sqlcnxreturnself._cnx.logged_userdefcursor(self):ifself._cnxisNone:self._cnx=self.source._sqlcnxreturnself._cnx.cursor()defcommit(self):ifself._cnxisnotNone:self._cnx.commit()defrollback(self):ifself._cnxisnotNone:self._cnx.rollback()defclose(self):ifself._cnxisnotNone:self._cnx.close()self._cnx=NoneclassSQLiteAbstractSource(AbstractSource):"""an abstract class for external sources using a sqlite database helper """sqlgen_class=SQLGenerator@classmethoddefset_nonsystem_types(cls):# those entities are only in this source, we don't want them in the# system sourceforetypeincls.support_entities:native.NONSYSTEM_ETYPES.add(etype)forrtypeincls.support_relations:native.NONSYSTEM_RELATIONS.add(rtype)options=(('helper-db-path',{'type':'string','default':None,'help':'path to the sqlite database file used to do queries on the \repository.','inputlevel':2,}),)def__init__(self,repo,appschema,source_config,*args,**kwargs):# the helper db is used to easy querying and will store everything but# actual file contentdbpath=source_config.get('helper-db-path')ifdbpathisNone:dbpath=join(repo.config.appdatahome,'%(uri)s.sqlite'%source_config)self.dbpath=dbpathself.sqladapter=SQLAdapterMixIn({'db-driver':'sqlite','db-name':dbpath})# those attributes have to be initialized before ancestor's __init__# which will call set_schemaself._need_sql_create=notexists(dbpath)self._need_full_import=self._need_sql_createAbstractSource.__init__(self,repo,appschema,source_config,*args,**kwargs)@propertydef_sqlcnx(self):# XXX: sqlite connections can only be used in the same thread, so# create a new one each time necessary. If it appears to be time# consuming, find another wayreturnself.sqladapter.get_connection()def_is_schema_complete(self):foretypeinself.support_entities:ifnotetypeinself.schema:self.warning('not ready to generate %s database, %s support missing from schema',self.uri,etype)returnFalseforrtypeinself.support_relations:ifnotrtypeinself.schema:self.warning('not ready to generate %s database, %s support missing from schema',self.uri,rtype)returnFalsereturnTruedef_create_database(self):fromyams.schema2sqlimporteschema2sql,rschema2sqlfromcubicweb.toolsutilsimportrestrict_perms_to_userself.warning('initializing sqlite database for %s source'%self.uri)cnx=self._sqlcnxcu=cnx.cursor()schema=self.schemaforetypeinself.support_entities:eschema=schema.eschema(etype)createsqls=eschema2sql(self.sqladapter.dbhelper,eschema,skip_relations=('data',),prefix=SQL_PREFIX)sqlexec(createsqls,cu,withpb=False)forrtypeinself.support_relations:rschema=schema.rschema(rtype)ifnotrschema.inlined:sqlexec(rschema2sql(rschema),cu,withpb=False)cnx.commit()cnx.close()self._need_sql_create=Falseifself.repo.config['uid']:fromlogilab.common.shellutilsimportchown# database file must be owned by the uid of the server processself.warning('set %s as owner of the database file',self.repo.config['uid'])chown(self.dbpath,self.repo.config['uid'])restrict_perms_to_user(self.dbpath,self.info)defset_schema(self,schema):super(SQLiteAbstractSource,self).set_schema(schema)ifself._need_sql_createandself._is_schema_complete()andself.dbpath:self._create_database()self.rqlsqlgen=self.sqlgen_class(schema,self.sqladapter.dbhelper)defget_connection(self):returnConnectionWrapper(self)defcheck_connection(self,cnx):"""check connection validity, return None if the connection is still valid else a new connection (called when the pool using the given connection is being attached to a session) always return the connection to reset eventually cached cursor """returncnxdefpool_reset(self,cnx):"""the pool using the given connection is being reseted from its current attached session: release the connection lock if the connection wrapper has a connection set """ifcnx._cnxisnotNone:cnx._cnx.close()# reset _cnx to ensure next thread using cnx will get a new# connectioncnx._cnx=Nonedefsyntax_tree_search(self,session,union,args=None,cachekey=None,varmap=None,debug=0):"""return result from this source for a rql query (actually from a rql syntax tree and a solution dictionary mapping each used variable to a possible type). If cachekey is given, the query necessary to fetch the results (but not the results themselves) may be cached using this key. """ifself._need_sql_create:return[]sql,query_args=self.rqlsqlgen.generate(union,args)ifserver.DEBUG:printself.uri,'SOURCE RQL',union.as_string()args=self.sqladapter.merge_args(args,query_args)cursor=session.pool[self.uri]self.doexec(cursor,sql,args)res=self.sqladapter.process_result(cursor)ifserver.DEBUG:print'------>',resreturnresdeflocal_add_entity(self,session,entity):"""insert the entity in the local database. This is not provided as add_entity implementation since usually source don't want to simply do this, so let raise NotImplementedError and the source implementor may use this method if necessary """attrs=self.sqladapter.preprocess_entity(entity)sql=self.sqladapter.sqlgen.insert(SQL_PREFIX+str(entity.e_schema),attrs)self.doexec(session.pool[self.uri],sql,attrs)defadd_entity(self,session,entity):"""add a new entity to the source"""raiseNotImplementedError()deflocal_update_entity(self,session,entity,attrs=None):"""update an entity in the source This is not provided as update_entity implementation since usually source don't want to simply do this, so let raise NotImplementedError and the source implementor may use this method if necessary """ifattrsisNone:attrs=self.sqladapter.preprocess_entity(entity)sql=self.sqladapter.sqlgen.update(SQL_PREFIX+str(entity.e_schema),attrs,[SQL_PREFIX+'eid'])self.doexec(session.pool[self.uri],sql,attrs)defupdate_entity(self,session,entity):"""update an entity in the source"""raiseNotImplementedError()defdelete_entity(self,session,etype,eid):"""delete an entity from the source this is not deleting a file in the svn but deleting entities from the source. Main usage is to delete repository content when a Repository entity is deleted. """attrs={SQL_PREFIX+'eid':eid}sql=self.sqladapter.sqlgen.delete(SQL_PREFIX+etype,attrs)self.doexec(session.pool[self.uri],sql,attrs)deflocal_add_relation(self,session,subject,rtype,object):"""add a relation to the source This is not provided as add_relation implementation since usually source don't want to simply do this, so let raise NotImplementedError and the source implementor may use this method if necessary """attrs={'eid_from':subject,'eid_to':object}sql=self.sqladapter.sqlgen.insert('%s_relation'%rtype,attrs)self.doexec(session.pool[self.uri],sql,attrs)defadd_relation(self,session,subject,rtype,object):"""add a relation to the source"""raiseNotImplementedError()defdelete_relation(self,session,subject,rtype,object):"""delete a relation from the source"""rschema=self.schema.rschema(rtype)ifrschema.inlined:ifsubjectinsession.query_data('pendingeids',()):returntable=SQL_PREFIX+session.describe(subject)[0]column=SQL_PREFIX+rtypesql='UPDATE %s SET %s=NULL WHERE %seid=%%(eid)s'%(table,column,SQL_PREFIX)attrs={'eid':subject}else:attrs={'eid_from':subject,'eid_to':object}sql=self.sqladapter.sqlgen.delete('%s_relation'%rtype,attrs)self.doexec(session.pool[self.uri],sql,attrs)defdoexec(self,cursor,query,args=None):"""Execute a query. it's a function just so that it shows up in profiling """#t1 = time()ifserver.DEBUG:print'exec',query,args#import sys#sys.stdout.flush()# str(query) to avoid error if it's an unicode stringtry:cursor.execute(str(query),args)exceptException,ex:self.critical("sql: %r\n args: %s\ndbms message: %r",query,args,ex.args[0])raise