/[thuban]/branches/WIP-pyshapelib-bramz/Thuban/Model/postgisdb.py
ViewVC logotype

Diff of /branches/WIP-pyshapelib-bramz/Thuban/Model/postgisdb.py

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 2096 by bh, Thu Mar 11 13:50:53 2004 UTC revision 2623 by bh, Mon May 9 18:12:12 2005 UTC
# Line 1  Line 1 
1  # Copyright (C) 2003, 2004 by Intevation GmbH  # Copyright (C) 2003, 2004, 2005 by Intevation GmbH
2  # Authors:  # Authors:
3  # Martin Mueller <[email protected]>  # Martin Mueller <[email protected]>
4  # Bernhard Herzog <[email protected]>  # Bernhard Herzog <[email protected]>
# Line 32  def psycopg_version(): Line 32  def psycopg_version():
32      return psycopg.__version__      return psycopg.__version__
33    
34  if psycopg is not None:  if psycopg is not None:
35        # type_map maps psycopg type objects.  It's a list of pairs since
36        # the psycopg type objects are unhashable.
37      type_map = [(psycopg.STRING, table.FIELDTYPE_STRING),      type_map = [(psycopg.STRING, table.FIELDTYPE_STRING),
38                  (psycopg.INTEGER, table.FIELDTYPE_INT),                  (psycopg.INTEGER, table.FIELDTYPE_INT),
39                    (psycopg.ROWID, table.FIELDTYPE_INT),
40                  (psycopg.FLOAT, table.FIELDTYPE_DOUBLE)]                  (psycopg.FLOAT, table.FIELDTYPE_DOUBLE)]
41    
42        # _raw_type_map maps the postgresql type constants to Thuban type
43        # constants.  This is very low level and postgresql specific and
44        # should be used only when necessary.
45        _raw_type_map = {}
46        def _fill_raw_type_map():
47            for psycopg_type, thuban_type in type_map:
48                for value in psycopg_type.values:
49                    _raw_type_map[value] = thuban_type
50        _fill_raw_type_map()
51    
52    
53  def quote_identifier(ident):  def quote_identifier(ident):
54      """Return a quoted version of the identifier ident.      """Return a quoted version of the identifier ident.
# Line 94  class PostGISConnection: Line 107  class PostGISConnection:
107          except psycopg.OperationalError, val:          except psycopg.OperationalError, val:
108              raise ConnectionError(str(val))              raise ConnectionError(str(val))
109    
110            # Use autocommit mode.  For simple reading of the database it's
111            # sufficient and we don't have to care much about error
112            # handling.  Without autocommit, an errors during a cursor's
113            # execute method requires a rollback on the connection,
114            # otherwise later queries with the same or other cursors sharing
115            # the same connection will lead to further errors ("ERROR:
116            # current transaction is aborted, commands ignored until end of
117            # transaction block")
118            self.connection.autocommit()
119    
120          # determine the OID for the geometry type. This is PostGIS          # determine the OID for the geometry type. This is PostGIS
121          # specific.          # specific.
122          cursor = self.connection.cursor()          cursor = self.connection.cursor()
# Line 135  class PostGISConnection: Line 158  class PostGISConnection:
158    
159      def GeometryTables(self):      def GeometryTables(self):
160          """Return a list with the names of all tables with a geometry column"""          """Return a list with the names of all tables with a geometry column"""
161    
162            # The query is basically taken from the psql v. 7.2.1.  When
163            # started with -E it prints the queries used for internal
164            # commands such as \d, which does mostly what we need here.
165          cursor = self.connection.cursor()          cursor = self.connection.cursor()
166          cursor.execute("SELECT f_table_name FROM geometry_columns;")          cursor.execute("SELECT c.relname FROM pg_class c"
167                           " WHERE c.relkind IN ('r', 'v')"
168                                 # Omit the system tables
169                                 " AND c.relname !~ '^pg_'"
170                                 # Omit the special PostGIS tables
171                                 " AND c.relname NOT IN ('geometry_columns',"
172                                                       " 'spatial_ref_sys')"
173                                " AND %d in (SELECT a.atttypid FROM pg_attribute a"
174                                           " WHERE a.attrelid = c.oid)"
175                           " ORDER BY c.relname;", (self.geometry_type,))
176          result = [row[0] for row in cursor.fetchall()]          result = [row[0] for row in cursor.fetchall()]
177          self.connection.commit()          self.connection.commit()
178          return result          return result
179    
180        def table_columns(self, tablename):
181            """Experimental: return information about the columns of a table
182    
183            Return value is a list of (name, type) pairs where name is the
184            name of the column and type either one of the field type columns
185            or the string 'geometry' indicating a geometry column.
186    
187            The intended use of this method is for table selection dialogs
188            which need to determine which columns are usable as id or
189            geometry columns respectively.  Suitable id columns will have
190            type FIELDTYPE_INT and geometry columns will have 'geometry'.
191            """
192            result = []
193            cursor = self.connection.cursor()
194    
195            # This query is taken basically from the \d command of psql
196            # 7.2.1
197            cursor.execute("SELECT a.attname, a.atttypid, a.attnum"
198                           " FROM pg_class c, pg_attribute a"
199                                " WHERE c.relname = %s AND a.attrelid = c.oid"
200                           " ORDER BY a.attnum;", (tablename,))
201    
202            for row in cursor.fetchall():
203                col_name, col_type, col_attnum = row
204                col = None
205                if col_attnum < 1:
206                    # It's a system column.  Only the OID is interesting
207                    # here
208                    if col_name == "oid":
209                        col = (col_name, _raw_type_map[col_type])
210                else:
211                    # If it's an integer
212                    thuban_type = _raw_type_map.get(col_type)
213                    if thuban_type is not None:
214                        col = (col_name, thuban_type)
215                    elif row[1] == self.geometry_type:
216                        col = (col_name, "geometry")
217                if col is not None:
218                    result.append(col)
219    
220            return result
221    
222      def cursor(self):      def cursor(self):
223          """Return a DB API 2.0 cursor for the database"""          """Return a DB API 2.0 cursor for the database"""
224          return self.connection.cursor()          return self.connection.cursor()
# Line 175  class PostGISTable: Line 253  class PostGISTable:
253      descriptions returned by Columns() and other methods.      descriptions returned by Columns() and other methods.
254      """      """
255    
256      def __init__(self, db, tablename, id_column = "gid"):      def __init__(self, db, tablename, id_column = None):
257          """Initialize the PostGISTable.          """Initialize the PostGISTable.
258    
259          The db parameter should be an instance of PostGISConnection and          The db parameter should be an instance of PostGISConnection and
# Line 184  class PostGISTable: Line 262  class PostGISTable:
262          The id_column parameter should be the name of a column in the          The id_column parameter should be the name of a column in the
263          table that can be used to identify rows.  The column must have          table that can be used to identify rows.  The column must have
264          the type integer and be unique and not null.          the type integer and be unique and not null.
265    
266            For backwards compatibility reasons, the id_column parameter is
267            optional.  If not given the table must have a column called
268            'gid' which is used as the id_column.  New code should always
269            provide this parameter.
270          """          """
271          self.db = db          self.db = db
272          self.tablename = tablename          self.tablename = tablename
273          # Tablename quoted for use in SQL statements.          # Tablename quoted for use in SQL statements.
274          self.quoted_tablename = quote_identifier(tablename)          self.quoted_tablename = quote_identifier(tablename)
275    
276            if not id_column:
277                id_column = "gid"
278          self.id_column = id_column          self.id_column = id_column
279          # id column name quoted for use in SQL statements.          # id column name quoted for use in SQL statements.
280          self.quoted_id_column = quote_identifier(id_column)          self.quoted_id_column = quote_identifier(id_column)
# Line 244  class PostGISTable: Line 329  class PostGISTable:
329          """Return the dbconnection used by the table"""          """Return the dbconnection used by the table"""
330          return self.db          return self.db
331    
332        def IDColumn(self):
333            """Return the column description object for the id column.
334    
335            If the oid column was used as the id column, the return value is
336            not one of the regular column objects that would be returned by
337            e.g. the Column() method, but it still has meaningful name
338            attribute.
339            """
340            if self.id_column == "oid":
341                return PostGISColumn(self.id_column, table.FIELDTYPE_INT, None)
342            return self.column_map[self.id_column]
343    
344      def TableName(self):      def TableName(self):
345          """Return the name of the table in the database"""          """Return the name of the table in the database"""
346          return self.tablename          return self.tablename
# Line 391  class PostGISShape: Line 488  class PostGISShape:
488    
489  shapetype_map = {"POLYGON": SHAPETYPE_POLYGON,  shapetype_map = {"POLYGON": SHAPETYPE_POLYGON,
490                   "MULTIPOLYGON": SHAPETYPE_POLYGON,                   "MULTIPOLYGON": SHAPETYPE_POLYGON,
491                     "LINESTRING": SHAPETYPE_ARC,
492                   "MULTILINESTRING": SHAPETYPE_ARC,                   "MULTILINESTRING": SHAPETYPE_ARC,
493                   "POINT": SHAPETYPE_POINT}                   "POINT": SHAPETYPE_POINT}
494    
# Line 427  class PostGISShapeStore(PostGISTable): Line 525  class PostGISShapeStore(PostGISTable):
525          self.quoted_geo_col = quote_identifier(self.geometry_column)          self.quoted_geo_col = quote_identifier(self.geometry_column)
526    
527      def _fetch_table_information(self):      def _fetch_table_information(self):
528          """Extend inherited method to retrieve the SRID"""          """Extend inherited method to retrieve the SRID and shape type"""
529          PostGISTable._fetch_table_information(self)          PostGISTable._fetch_table_information(self)
530    
531            # First, try to get it from the geometry_columns table.
532          cursor = self.db.cursor()          cursor = self.db.cursor()
533          cursor.execute("SELECT srid FROM geometry_columns"          cursor.execute("SELECT srid, type FROM geometry_columns"
534                         " WHERE f_table_name = %s AND f_geometry_column=%s",                         " WHERE f_table_name = %s AND f_geometry_column=%s",
535                         (self.tablename, self.geometry_column))                         (self.tablename, self.geometry_column))
536          self.srid = cursor.fetchone()[0]          row = cursor.fetchone()
537            if row is not None:
538                self.srid = row[0]
539                self.shape_type = shapetype_map.get(row[1])
540                return
541    
542            # The table is probably really a view and thus not in
543            # geometry_columns.  Use a different approach
544            cursor = self.db.cursor()
545            cursor.execute("SELECT DISTINCT SRID(%s) FROM %s;" %
546                           (quote_identifier(self.geometry_column),
547                            self.tablename))
548            row = cursor.fetchone()
549            if row is not None:
550                self.srid = row[0]
551                # Try to see whether there's another one
552                row = cursor.fetchone()
553                if row is not None:
554                    # There are at least two different srids.  We don't
555                    # support that
556                    self.srid = None
557    
558            cursor = self.db.cursor()
559            cursor.execute("SELECT DISTINCT GeometryType(%s) FROM %s;"
560                           % (quote_identifier(self.geometry_column),
561                              self.tablename))
562            row = cursor.fetchone()
563            if row is not None:
564                self.shape_type = shapetype_map.get(row[0])
565                # Try to see whether there's another one
566                row = cursor.fetchone()
567                if row is not None:
568                    # There are at least two different srids.  We don't
569                    # support that
570                    self.shape_type = None
571    
572      def _create_col_from_description(self, index, description):      def _create_col_from_description(self, index, description):
573          """Extend the inherited method to find geometry columns          """Extend the inherited method to find geometry columns
# Line 475  class PostGISShapeStore(PostGISTable): Line 609  class PostGISShapeStore(PostGISTable):
609          """          """
610          return None          return None
611    
612        def GeometryColumn(self):
613            """Return the column description object for the geometry column
614    
615            There's currently no FIELDTYPE constant for this column, so the
616            return value is not a regular column object that could also be
617            returned from e.g. the Column() method.  Only the name attribute
618            of the return value is meaningful at the moment.
619            """
620            return PostGISColumn(self.geometry_column, None, None)
621    
622      def ShapeType(self):      def ShapeType(self):
623          """Return the type of the shapes in the shapestore."""          """Return the type of the shapes in the shapestore."""
624          cursor = self.db.cursor()          return self.shape_type
         cursor.execute("SELECT type FROM geometry_columns WHERE"  
                        " f_table_name=%s AND f_geometry_column=%s",  
                        (self.tablename, self.geometry_column))  
         result = cursor.fetchone()[0]  
         cursor.close()  
         return shapetype_map[result]  
625    
626      def RawShapeFormat(self):      def RawShapeFormat(self):
627          """Return the raw data format of the shape data.          """Return the raw data format of the shape data.

Legend:
Removed from v.2096  
changed lines
  Added in v.2623

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26