diff -r f4d1d5d9ccbb -r 90f2f20367bc server/sources/native.py --- a/server/sources/native.py Tue Jul 27 12:36:03 2010 +0200 +++ b/server/sources/native.py Wed Nov 03 16:38:28 2010 +0100 @@ -34,6 +34,7 @@ from base64 import b64decode, b64encode from contextlib import contextmanager from os.path import abspath +import re from logilab.common.compat import any from logilab.common.cache import Cache @@ -42,7 +43,9 @@ from logilab.common.shellutils import getlogin from logilab.database import get_db_helper -from cubicweb import UnknownEid, AuthenticationError, ValidationError, Binary +from yams import schema2sql as y2sql + +from cubicweb import UnknownEid, AuthenticationError, ValidationError, Binary, UniqueTogetherError from cubicweb import transaction as tx, server, neg_role from cubicweb.schema import VIRTUAL_RTYPES from cubicweb.cwconfig import CubicWebNoAppConfiguration @@ -127,6 +130,25 @@ restr = '(%s)' % ' OR '.join(clauses) return '%s WHERE %s' % (select, restr) +def rdef_table_column(rdef): + """return table and column used to store the given relation definition in + the database + """ + return (SQL_PREFIX + str(rdef.subject), + SQL_PREFIX + str(rdef.rtype)) + +def rdef_physical_info(dbhelper, rdef): + """return backend type and a boolean flag if NULL values should be allowed + for a given relation definition + """ + if rdef.object.final: + ttype = rdef.object + else: + ttype = 'Int' # eid type + coltype = y2sql.type_from_constraints(dbhelper, ttype, + rdef.constraints, creating=False) + allownull = rdef.cardinality[0] != '1' + return coltype, allownull class UndoException(Exception): """something went wrong during undoing""" @@ -193,7 +215,7 @@ 'default': 'postgres', # XXX use choice type 'help': 'database driver (postgres, mysql, sqlite, sqlserver2005)', - 'group': 'native-source', 'level': 1, + 'group': 'native-source', 'level': 0, }), ('db-host', {'type' : 'string', @@ -488,7 +510,7 @@ def manual_insert(self, results, table, session): """insert given result into a temporary table on the system source""" if server.DEBUG & server.DBG_RQL: - print ' manual insertion of', results, 'into', table + print ' manual insertion of', len(results), 'results into', table if not results: return query_args = ['%%(%s)s' % i for i in xrange(len(results[0]))] @@ -649,6 +671,21 @@ self.critical('transaction has been rollbacked') except: pass + if ex.__class__.__name__ == 'IntegrityError': + # need string comparison because of various backends + for arg in ex.args: + mo = re.search('unique_cw_[^ ]+_idx', arg) + if mo is not None: + index_name = mo.group(0) + elements = index_name.rstrip('_idx').split('_cw_')[1:] + etype = elements[0] + rtypes = elements[1:] + raise UniqueTogetherError(etype, rtypes) + mo = re.search('columns (.*) are not unique', arg) + if mo is not None: # sqlite in use + rtypes = [c.strip().lstrip('cw_') for c in mo.group(1).split(',')] + etype = '???' + raise UniqueTogetherError(etype, rtypes) raise return cursor @@ -678,6 +715,47 @@ # short cut to method requiring advanced db helper usage ################## + def update_rdef_column(self, session, rdef): + """update physical column for a relation definition (final or inlined) + """ + table, column = rdef_table_column(rdef) + coltype, allownull = rdef_physical_info(self.dbhelper, rdef) + if not self.dbhelper.alter_column_support: + self.error("backend can't alter %s.%s to %s%s", table, column, coltype, + not allownull and 'NOT NULL' or '') + return + self.dbhelper.change_col_type(LogCursor(session.pool[self.uri]), + table, column, coltype, allownull) + self.info('altered %s.%s: now %s%s', table, column, coltype, + not allownull and 'NOT NULL' or '') + + def update_rdef_null_allowed(self, session, rdef): + """update NULL / NOT NULL of physical column for a relation definition + (final or inlined) + """ + if not self.dbhelper.alter_column_support: + # not supported (and NOT NULL not set by yams in that case, so no + # worry) + return + table, column = rdef_table_column(rdef) + coltype, allownull = rdef_physical_info(self.dbhelper, rdef) + self.dbhelper.set_null_allowed(LogCursor(session.pool[self.uri]), + table, column, coltype, allownull) + + def update_rdef_indexed(self, session, rdef): + table, column = rdef_table_column(rdef) + if rdef.indexed: + self.create_index(session, table, column) + else: + self.drop_index(session, table, column) + + def update_rdef_unique(self, session, rdef): + table, column = rdef_table_column(rdef) + if rdef.constraint_by_type('UniqueConstraint'): + self.create_index(session, table, column, unique=True) + else: + self.drop_index(session, table, column, unique=True) + def create_index(self, session, table, column, unique=False): cursor = LogCursor(session.pool[self.uri]) self.dbhelper.create_index(cursor, table, column, unique) @@ -686,14 +764,6 @@ cursor = LogCursor(session.pool[self.uri]) self.dbhelper.drop_index(cursor, table, column, unique) - def change_col_type(self, session, table, column, coltype, null_allowed): - cursor = LogCursor(session.pool[self.uri]) - self.dbhelper.change_col_type(cursor, table, column, coltype, null_allowed) - - def set_null_allowed(self, session, table, column, coltype, null_allowed): - cursor = LogCursor(session.pool[self.uri]) - self.dbhelper.set_null_allowed(cursor, table, column, coltype, null_allowed) - # system source interface ################################################# def eid_type_source(self, session, eid): @@ -801,7 +871,6 @@ cnx.commit() return eid - def add_info(self, session, entity, source, extid, complete): """add type and source info for an eid into the system table""" # begin by inserting eid/type/source/extid into the entities table @@ -1079,10 +1148,10 @@ entity[rtype] = unicode(value, session.encoding, 'replace') else: entity[rtype] = value - entity.set_eid(eid) + entity.eid = eid session.repo.init_entity_caches(session, entity, self) entity.edited_attributes = set(entity) - entity.check() + entity._cw_check() self.repo.hm.call_hooks('before_add_entity', session, entity=entity) # restore the entity action.changes['cw_eid'] = eid @@ -1149,7 +1218,7 @@ return [session._( "Can't undo creation of entity %(eid)s of type %(etype)s, type " "no more supported" % {'eid': eid, 'etype': etype})] - entity.set_eid(eid) + entity.eid = eid # for proper eid/type cache update hook.set_operation(session, 'pendingeids', eid, CleanupDeletedEidsCacheOp) @@ -1237,7 +1306,8 @@ try: # use cursor_index_object, not cursor_reindex_object since # unindexing done in the FTIndexEntityOp - self.dbhelper.cursor_index_object(entity.eid, entity, + self.dbhelper.cursor_index_object(entity.eid, + entity.cw_adapt_to('IFTIndexable'), session.pool['system']) except Exception: # let KeyboardInterrupt / SystemExit propagate self.exception('error while reindexing %s', entity) @@ -1262,7 +1332,8 @@ # processed return done.add(eid) - for container in session.entity_from_eid(eid).fti_containers(): + iftindexable = session.entity_from_eid(eid).cw_adapt_to('IFTIndexable') + for container in iftindexable.fti_containers(): source.fti_unindex_entity(session, container.eid) source.fti_index_entity(session, container)