# HG changeset patch # User Sylvain Thénault # Date 1449679458 -3600 # Node ID f8417bd135edb6282beb5ae0955187ace3dd8c07 # Parent 14ba505fb652ac3a4e753ca0d9fdc43d4ed90d42 [server, hooks] allow callable in dbh.TYPE_MAPPING This is necessary for cubes introducing new custom final types whose generated sql may depend on some extra configuration, avoiding ugly-buggy hack as done currently by the postgis cube. Closes #7569998 diff -r 14ba505fb652 -r f8417bd135ed hooks/syncschema.py --- a/hooks/syncschema.py Wed Dec 09 17:44:17 2015 +0100 +++ b/hooks/syncschema.py Wed Dec 09 17:44:18 2015 +0100 @@ -440,12 +440,15 @@ # probably buggy) rdef = self.cnx.vreg.schema.rschema(rdefdef.name).rdefs[rdefdef.subject, rdefdef.object] assert rdef.infered + else: + rdef = self.cnx.vreg.schema.rschema(rdefdef.name).rdefs[rdefdef.subject, rdefdef.object] + self.cnx.execute('SET X ordernum Y+1 ' 'WHERE X from_entity SE, SE eid %(se)s, X ordernum Y, ' 'X ordernum >= %(order)s, NOT X eid %(x)s', {'x': entity.eid, 'se': fromentity.eid, 'order': entity.ordernum or 0}) - return rdefdef + return rdefdef, rdef def precommit_event(self): cnx = self.cnx @@ -465,11 +468,10 @@ if hasattr(entity, 'formula'): props['formula'] = entity.formula # update the in-memory schema first - rdefdef = self.init_rdef(**props) + rdefdef, rdef = self.init_rdef(**props) # then make necessary changes to the system source database syssource = cnx.repo.system_source - attrtype = y2sql.type_from_constraints( - syssource.dbhelper, rdefdef.object, rdefdef.constraints) + attrtype = y2sql.type_from_rdef(syssource.dbhelper, rdef) # XXX should be moved somehow into lgdb: sqlite doesn't support to # add a new column with UNIQUE, it should be added after the ALTER TABLE # using ADD INDEX @@ -549,7 +551,7 @@ cnx = self.cnx entity = self.entity # update the in-memory schema first - rdefdef = self.init_rdef(composite=entity.composite) + rdefdef, rdef = self.init_rdef(composite=entity.composite) # then make necessary changes to the system source database schema = cnx.vreg.schema rtype = rdefdef.name diff -r 14ba505fb652 -r f8417bd135ed server/migractions.py --- a/server/migractions.py Wed Dec 09 17:44:17 2015 +0100 +++ b/server/migractions.py Wed Dec 09 17:44:18 2015 +0100 @@ -60,7 +60,7 @@ from cubicweb import repoapi from cubicweb.migration import MigrationHelper, yes from cubicweb.server import hook, schemaserial as ss -from cubicweb.server.schema2sql import eschema2sql, rschema2sql, unique_index_name +from cubicweb.server.schema2sql import eschema2sql, rschema2sql, unique_index_name, sql_type from cubicweb.server.utils import manager_userpasswd from cubicweb.server.sqlutils import sqlexec, SQL_PREFIX @@ -1508,8 +1508,10 @@ "WHERE cw_eid=%s") % (newtype, rdef.eid) self.sqlexec(sql, ask_confirm=False) dbhelper = self.repo.system_source.dbhelper - sqltype = dbhelper.TYPE_MAPPING[newtype] + newrdef = self.fs_schema.rschema(attr).rdef(etype, newtype) + sqltype = sql_type(dbhelper, newrdef) cursor = self.cnx.cnxset.cu + # consider former cardinality by design, since cardinality change is not handled here allownull = rdef.cardinality[0] != '1' dbhelper.change_col_type(cursor, 'cw_%s' % etype, 'cw_%s' % attr, sqltype, allownull) if commit: diff -r 14ba505fb652 -r f8417bd135ed server/schema2sql.py --- a/server/schema2sql.py Wed Dec 09 17:44:17 2015 +0100 +++ b/server/schema2sql.py Wed Dec 09 17:44:18 2015 +0100 @@ -190,8 +190,7 @@ """write an attribute schema as SQL statements to stdout""" attr = rschema.type rdef = rschema.rdef(eschema.type, aschema.type) - sqltype = type_from_constraints(dbhelper, aschema.type, rdef.constraints, - creating) + sqltype = type_from_rdef(dbhelper, rdef, creating) if SET_DEFAULT: default = eschema.default(attr) if default is not None: @@ -215,11 +214,11 @@ return sqltype -def type_from_constraints(dbhelper, etype, constraints, creating=True): - """return a sql type string corresponding to the constraints""" - constraints = list(constraints) +def type_from_rdef(dbhelper, rdef, creating=True): + """return a sql type string corresponding to the relation definition""" + constraints = list(rdef.constraints) unique, sqltype = False, None - if etype == 'String': + if rdef.object.type == 'String': for constraint in constraints: if isinstance(constraint, SizeConstraint): if constraint.max is not None: @@ -229,12 +228,19 @@ elif isinstance(constraint, UniqueConstraint): unique = True if sqltype is None: - sqltype = dbhelper.TYPE_MAPPING[etype] + sqltype = sql_type(dbhelper, rdef) if creating and unique: sqltype += ' UNIQUE' return sqltype +def sql_type(dbhelper, rdef): + sqltype = dbhelper.TYPE_MAPPING[rdef.object] + if callable(sqltype): + sqltype = sqltype(rdef) + return sqltype + + _SQL_SCHEMA = """ CREATE TABLE %(table)s ( eid_from INTEGER NOT NULL REFERENCES entities (eid), diff -r 14ba505fb652 -r f8417bd135ed server/sources/native.py --- a/server/sources/native.py Wed Dec 09 17:44:17 2015 +0100 +++ b/server/sources/native.py Wed Dec 09 17:44:18 2015 +0100 @@ -119,12 +119,9 @@ """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) + if not rdef.object.final: + return dbhelper.TYPE_MAPPING['Int'] + coltype = y2sql.type_from_rdef(dbhelper, rdef, creating=False) allownull = rdef.cardinality[0] != '1' return coltype, allownull diff -r 14ba505fb652 -r f8417bd135ed server/test/data-migractions/cubes/fakegis/site_cubicweb.py --- a/server/test/data-migractions/cubes/fakegis/site_cubicweb.py Wed Dec 09 17:44:17 2015 +0100 +++ b/server/test/data-migractions/cubes/fakegis/site_cubicweb.py Wed Dec 09 17:44:18 2015 +0100 @@ -7,7 +7,23 @@ # Add the datatype to the helper mapping pghelper = get_db_helper('postgres') -pghelper.TYPE_MAPPING['Geometry'] = 'geometry' + + +def pg_geometry_sqltype(rdef): + """Return a PostgreSQL column type corresponding to rdef's geom_type, srid + and coord_dimension. + """ + target_geom_type = rdef.geom_type + if rdef.coord_dimension >= 3: # XXX: handle 2D+M + target_geom_type += 'Z' + if rdef.coord_dimension == 4: + target_geom_type += 'M' + assert target_geom_type + assert rdef.srid + return 'geometry(%s, %s)' % (target_geom_type, rdef.srid) + + +pghelper.TYPE_MAPPING['Geometry'] = pg_geometry_sqltype # Add a converter for Geometry diff -r 14ba505fb652 -r f8417bd135ed server/test/unittest_migractions.py --- a/server/test/unittest_migractions.py Wed Dec 09 17:44:17 2015 +0100 +++ b/server/test/unittest_migractions.py Wed Dec 09 17:44:18 2015 +0100 @@ -298,6 +298,9 @@ self.assertEqual(rdef.geom_type, 'GEOMETRYCOLLECTION') self.assertEqual(rdef.coord_dimension, 2) self.assertEqual(rdef.srid, 4326) + #self.assertEqual(mh.sqlexec('SELECT pg_typeof("cw_geometry") FROM cw_Location'), '') + fields = self.table_schema(mh, '%sLocation' % SQL_PREFIX) + self.assertEqual(fields['%sgeometry' % SQL_PREFIX], ('USER-DEFINED', None)) # XXX def test_add_drop_entity_type(self): with self.mh() as (cnx, mh):