From 3460f5820012825744da2269ca9945b8c5edef8b Mon Sep 17 00:00:00 2001 From: Yannick Vaucher Date: Tue, 1 Oct 2019 09:36:31 +0200 Subject: [PATCH] Fix geometry field creation missing configurations After changing the field in state `base` instead of `manual`. Those fields were created without configuration. srid would be 0 gemetry type in geometry_columns table would be GEOMETRY instead of POINT, LINESTRING, ... This fixes the creation of geometry to use AddGeometryColumn again. --- base_geoengine/fields.py | 112 +++++++++++++++++++----------------- base_geoengine/geo_db.py | 39 +++++++++++++ base_geoengine/geo_model.py | 33 ----------- 3 files changed, 98 insertions(+), 86 deletions(-) diff --git a/base_geoengine/fields.py b/base_geoengine/fields.py index 0c22753645..b2620ff481 100644 --- a/base_geoengine/fields.py +++ b/base_geoengine/fields.py @@ -5,8 +5,10 @@ from operator import attrgetter from odoo import fields, _ +from odoo.tools import sql from .geo_helper import geo_convertion_helper as convert +from .geo_db import create_geo_column logger = logging.getLogger(__name__) try: @@ -94,26 +96,6 @@ def load_geo(cls, wkb): """Load geometry into browse record after read was done""" return wkbloads(wkb, hex=True) if wkb else False - def create_geo_column(self, cr, col_name, table, model): - """Create a columns of type the geom""" - try: - cr.execute("SELECT AddGeometryColumn( %s, %s, %s, %s, %s)", - (table, - col_name, - self.srid, - self.geo_type, - self.dim)) - self._create_index(cr, table, col_name) - except Exception: - cr.rollback() - logger.exception('Cannot create column %s table %s:', - col_name, table) - raise - finally: - cr.commit() - - return True - def entry_to_shape(self, value, same_type=False): """Transform input into an object""" shape = convert.value_to_shape(value) @@ -124,63 +106,87 @@ def entry_to_shape(self, value, same_type=False): self.geo_type.lower())) return shape - def _postgis_index_name(self, table, col_name): - return "%s_%s_gist_index" % (table, col_name) - - def _create_index(self, cr, table, col_name): - if self.gist_index: - try: - cr.execute("CREATE INDEX %s ON %s USING GIST ( %s )" % - (self._postgis_index_name(table, col_name), - table, - col_name)) - except Exception: - cr.rollback() - logger.exception( - 'Cannot create gist index for col %s table %s:', - col_name, table) - raise - finally: - cr.commit() - - def update_geo_column(self, cr, col_name, table, model): + def update_geo_db_column(self, model): """Update the column type in the database. """ + cr = model._cr query = ("""SELECT srid, type, coord_dimension FROM geometry_columns WHERE f_table_name = %s AND f_geometry_column = %s""") - cr.execute(query, (table, col_name)) + cr.execute(query, (model._table, self.name)) check_data = cr.fetchone() if not check_data: raise TypeError( - "geometry_columns table seems to be corrupted. " - "SRID check is not possible") + "geometry_columns table seems to be corrupted." + " SRID check is not possible") if check_data[0] != self.srid: raise TypeError( - "Reprojection of column is not implemented" - "We can not change srid %s to %s" % ( + "Reprojection of column is not implemented." + " We can not change srid %s to %s" % ( self.srid, check_data[0])) - if check_data[1] != self.geo_type: + elif check_data[1] != self.geo_type: raise TypeError( - "Geo type modification is not implemented" - "We can not change type %s to %s" % ( + "Geo type modification is not implemented." + " We can not change type %s to %s" % ( check_data[1], self.geo_type)) - if check_data[2] != self.dim: + elif check_data[2] != self.dim: raise TypeError( - "Geo dimention modification is not implemented" - "We can not change dimention %s to %s" % ( + "Geo dimention modification is not implemented." + " We can not change dimention %s to %s" % ( check_data[2], self.dim)) if self.gist_index: cr.execute( "SELECT indexname FROM pg_indexes WHERE indexname = %s", - (self._postgis_index_name(table, col_name),)) + (self._postgis_index_name(model._table, self.name),)) index = cr.fetchone() if index: return True - self._create_index(cr, table, col_name) + self._create_index(cr, model._table, self.name) return True + def update_db_column(self, model, column): + """ Create/update the column corresponding to ``self``. + + For creation of geo column + + :param model: an instance of the field's model + :param column: the column's configuration (dict) + if it exists, or ``None`` + """ + # the column does not exist, create it + + if not column: + create_geo_column( + model._cr, + model._table, + self.name, + self.geo_type, + self.srid, + self.dim, + self.string) + return + + if column['udt_name'] == self.column_type[0]: + return + + self.update_geo_db_column(model) + + if column['udt_name'] in self.column_cast_from: + sql.convert_column( + model._cr, model._table, self.name, self.column_type[1]) + else: + newname = (self.name + '_moved{}').format + i = 0 + while sql.column_exists( + model._cr, model._table, newname(i) + ): + i += 1 + if column['is_nullable'] == 'NO': + sql.drop_not_null(model._cr, model._table, self.name) + sql.rename_column(model._cr, model._table, self.name, newname(i)) + sql.create_column(model._cr, model._table, self.name, self.column_type[1], self.string) + class GeoLine(GeoField): """Field for POSTGIS geometry Line type""" diff --git a/base_geoengine/geo_db.py b/base_geoengine/geo_db.py index 24d68bfe4f..cddf984d6a 100644 --- a/base_geoengine/geo_db.py +++ b/base_geoengine/geo_db.py @@ -5,8 +5,10 @@ from odoo import _ from odoo.exceptions import MissingError +from odoo.tools import sql logger = logging.getLogger('geoengine.sql') +_schema = logging.getLogger('odoo.schema') def init_postgis(cr): @@ -45,3 +47,40 @@ def init_postgis(cr): "CREATE EXTENSION postgis_topology;\n" ) ) + + +def create_geo_column( + cr, tablename, columnname, geotype, srid, dim, comment=None): + """ Create a geometry column with the given type. + + :params: srid: geometry's projection srid + :params: dim: geometry's dimension (2D or 3D) + """ + cr.execute("SELECT AddGeometryColumn( %s, %s, %s, %s, %s)", + (tablename, + columnname, + srid, + geotype, + dim)) + if comment: + cr.execute('COMMENT ON COLUMN "{}"."{}" IS %s'.format( + tablename, columnname), (comment,)) + _schema.debug( + "Table %r: added geometry column %r of type %s", + tablename, columnname, geotype) + + +def _postgis_index_name(table, col_name): + return "%s_%s_gist_index" % (table, col_name) + + +def create_geo_index(cr, columnname, tablename): + """ Create the given index unless it exists. """ + indexname = _postgis_index_name(tablename, columnname) + if sql.index_exists(cr, indexname): + return + cr.execute("CREATE INDEX %s ON %s USING GIST ( %s )" % + (indexname, + tablename, + columnname)) + _schema.debug("Table %r: created index %r (%s)", tablename, indexname, args) diff --git a/base_geoengine/geo_model.py b/base_geoengine/geo_model.py index 018477cc64..8568dc232a 100644 --- a/base_geoengine/geo_model.py +++ b/base_geoengine/geo_model.py @@ -19,37 +19,6 @@ class GeoModel(models.AbstractModel): # Array of ash that define layer and data to use _georepr = [] - @api.model_cr_context - def _auto_init(self): - """Initialize the columns in dB and Create the GIST index - only create and update supported - - We override the base methid because creation of fields in DB is not - actually delegated to the field it self but to the ORM _auto_init - function - """ - cr = self._cr - - geo_fields = {} - for f_name, field in self._fields.items(): - if field.type.startswith('geo_'): - geo_fields[f_name] = field - res = super()._auto_init() - if self._abstract: - return res - - # Create geo columns - column_data = tools.table_columns(cr, self._table) - - for f_name, geo_field in geo_fields.items(): - if geo_field.compute and not geo_field.store: - continue - fct = geo_field.create_geo_column - if f_name in column_data: - fct = geo_field.update_geo_column - fct(cr, f_name, self._table, self._name) - return res - @api.model def fields_get(self, allfields=None, attributes=None): """Add geo_type definition for geo fields""" @@ -110,8 +79,6 @@ def set_field_real_name(in_tuple): 'default_extent': view.default_extent or DEFAULT_EXTENT, 'default_zoom': view.default_zoom, } - # XXX still the case ? - # TODO find why context in read does not work with webclient for layer in view.raster_layer_ids: layer_dict = layer.read()[0] res['geoengine_layers']['backgrounds'].append(layer_dict)