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

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

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

revision 841 by bh, Tue May 6 17:12:53 2003 UTC revision 1026 by frank, Mon May 26 11:46:42 2003 UTC
# Line 22  import os Line 22  import os
22  import weakref  import weakref
23  from sqlite import connect  from sqlite import connect
24    
25    from base import TitledObject
26    
27  import table  import table
28    
29  sql_type_map = {  sql_type_map = {
# Line 161  class TransientTableBase(table.OldTableI Line 163  class TransientTableBase(table.OldTableI
163          return self.column_map.has_key(col)          return self.column_map.has_key(col)
164    
165      def ReadRowAsDict(self, index):      def ReadRowAsDict(self, index):
166            # Implementation Strategy: Executing a completely new select
167            # statement every time this method is called is too slow. The
168            # most important usage is to read the records more or less
169            # sequentially. This happens e.g. when drawing a layer with a
170            # classification where the shapes are drawn in order of the
171            # shape ids. Another pattern is that the same row is requested
172            # several times in a row. This happens in the table view, for
173            # instance.
174    
175            # We can exploit this to make access faster by having one cursor
176            # open all the time and keeping the last row read around in case
177            # the same row is accessed again the next time and if the row
178            # index is larger than the row we have read last we simply fetch
179            # rows from the cursor until we've reached the requested row. If
180            # the requested row index is smaller then we start a new cursor.
181    
182            # FIXME: So far this scheme seems to work well enough. Obvious
183            # improvements would be to start the cursor at exactly the
184            # requested row (should be efficient and easy to do now that the
185            # id is the primary key) and to perhaps to also start a new
186            # cursor if the requested index is much larger than the last row
187            # so that we don't read and discard lots of the rows.
188    
189            # Check whether we have to start a new cursor
190          if self.read_record_cursor is None or index <self.read_record_last_row:          if self.read_record_cursor is None or index <self.read_record_last_row:
191              stmt = ("SELECT %s FROM %s;"              stmt = ("SELECT %s FROM %s;"
192                      % (", ".join([c.internal_name for c in self.columns]),                      % (", ".join([c.internal_name for c in self.columns]),
# Line 184  class TransientTableBase(table.OldTableI Line 210  class TransientTableBase(table.OldTableI
210          self.read_record_last_row = index          self.read_record_last_row = index
211          return dict(zip(self.orig_names, result))          return dict(zip(self.orig_names, result))
212    
213        def ReadValue(self, row, col):
214            """Return the value of the specified row and column
215    
216            The col parameter may be the index of the column or its name.
217            """
218            # Depending on the actual access patterns of the table data, it
219            # might be a bit faster in some circumstances to not implement
220            # this via ReadRowAsDict, but this simple implementation should
221            # be fast enough for most purposes.
222            return self.ReadRowAsDict(row)[self.column_map[col].name]
223    
224      def ValueRange(self, col):      def ValueRange(self, col):
225          col = self.column_map[col]          col = self.column_map[col]
226          iname = col.internal_name          iname = col.internal_name
# Line 205  class TransientTableBase(table.OldTableI Line 242  class TransientTableBase(table.OldTableI
242              result.append(row[0])              result.append(row[0])
243          return result          return result
244    
245        def Width(self, col):
246            """Return the maximum width of values in the column
247    
248            The return value is the the maximum length of string representation
249            of the values in the column (represented by index or name)."""
250            max = 0
251            
252            type  = self.column_map[col].type
253            iname = self.column_map[col].internal_name
254            cursor = self.db.cursor()
255            cursor.execute("SELECT %s FROM %s;" % (iname, self.tablename))
256            values = [ i[0] for i in cursor.fetchall()]
257            if not values:
258                return None
259    
260            if type == sql_type_map[table.FIELDTYPE_DOUBLE]:
261                prec = self.Precision(col)
262                format = "%%.%df" % prec
263            elif type == sql_type_map[table.FIELDTYPE_INT]:
264                format = "%d"
265            else:
266                format = "%s"
267            for value in values:
268                if value is None: continue
269                l = len(format % value)
270                if l > max:
271                    max = l
272    
273            return max
274    
275        def Precision(self, col):
276            """Return the precision of the column
277    
278            The return value is the maximum number of numeric characters after the
279            decimal if column type is double. Else precision zero is returned.
280            The column can be represented by index or name.
281            """
282        
283            type  = self.column_map[col].type
284            if type == sql_type_map[table.FIELDTYPE_DOUBLE]:
285                iname = self.column_map[col].internal_name
286                cursor = self.db.cursor()
287                cursor.execute("SELECT %s FROM %s;" % (iname, self.tablename))
288                values = [ i[0] for i in cursor.fetchall()]
289                if not values:
290                    return 0
291                
292                max = 0
293                for value in values:
294                    if value is None: continue
295                    l = len(str(value % 1))
296                    if l > max:
297                        max = l
298                if max > 2:
299                    return max - 2
300                else:
301                    return 0
302            else:
303                return 0
304    
305      def SimpleQuery(self, left, comparison, right):      def SimpleQuery(self, left, comparison, right):
306          """Return the indices of all rows that matching a condition.          """Return the indices of all rows that matching a condition.
307    
# Line 245  class TransientTableBase(table.OldTableI Line 342  class TransientTableBase(table.OldTableI
342              result.append(row[0])              result.append(row[0])
343          return result          return result
344    
345        def Dependencies(self):
346            """Placeholder for a method in a derived class.
347    
348            Return a sequence with the tables and other data objects that
349            self depends on.
350            """
351            raise NotImplementedError
352    
353  class TransientTable(TransientTableBase):  
354    class TransientTable(TitledObject, TransientTableBase):
355    
356      """A Table in a transient DB that starts as the copy of a Thuban Table."""      """A Table in a transient DB that starts as the copy of a Thuban Table."""
357    
# Line 257  class TransientTable(TransientTableBase) Line 362  class TransientTable(TransientTableBase)
362          interface.          interface.
363          """          """
364          TransientTableBase.__init__(self, transient_db)          TransientTableBase.__init__(self, transient_db)
365            TitledObject.__init__(self, table.Title())
366          self.create(table)          self.create(table)
367    
368      def create(self, table):      def create(self, table):
# Line 290  class TransientTable(TransientTableBase) Line 396  class TransientTable(TransientTableBase)
396    
397    
398    
399  class TransientJoinedTable(TransientTableBase):  class TransientJoinedTable(TitledObject, TransientTableBase):
400    
401      """A Table in the transient DB that contains a join of two tables"""      """A Table in the transient DB that contains a join of two tables"""
402    
403      def __init__(self, transient_db, left_table, left_field,      def __init__(self, transient_db, left_table, left_field,
404                   right_table, right_field = None):                   right_table, right_field = None, outer_join = False):
405          """Create a new table in the transient DB as a join of two tables.          """Create a new table in the transient DB as a join of two tables.
406    
407          Both input tables, left_table and right_table must have a          Both input tables, left_table and right_table must have a
408          transient_table method that returns a table object for a table          transient_table method that returns a table object for a table
409          in the trnsient database. The join is performed on the condition          in the transient database. The join is performed on the condition
410          that the value of the left_field column the the left table is          that the value of the left_field column the the left table is
411          equal to the value of the right_field in the right_table.          equal to the value of the right_field in the right_table.
412    
# Line 312  class TransientJoinedTable(TransientTabl Line 418  class TransientJoinedTable(TransientTabl
418          names of the input tables without having to create prefixes.          names of the input tables without having to create prefixes.
419          """          """
420          TransientTableBase.__init__(self, transient_db)          TransientTableBase.__init__(self, transient_db)
421            self.dependencies = (left_table, right_table)
422          self.left_table = left_table.transient_table()          self.left_table = left_table.transient_table()
423          self.left_field = left_field          self.left_field = left_field
424          self.right_table = right_table.transient_table()          self.right_table = right_table.transient_table()
# Line 319  class TransientJoinedTable(TransientTabl Line 426  class TransientJoinedTable(TransientTabl
426              self.right_field = right_field              self.right_field = right_field
427          else:          else:
428              self.right_field = self.left_field              self.right_field = self.left_field
429            self.outer_join = outer_join
430    
431            title = "Join of %(left)s and %(right)s" \
432                    % {"left": self.left_table.Title(),
433                       "right": self.right_table.Title()}
434            TitledObject.__init__(self, title)
435    
436          self.create()          self.create()
437    
438      def create(self):      def create(self):
# Line 341  class TransientJoinedTable(TransientTabl Line 455  class TransientJoinedTable(TransientTabl
455    
456          # Copy the joined data to the table.          # Copy the joined data to the table.
457          internal_names = [col.internal_name for col in self.columns]          internal_names = [col.internal_name for col in self.columns]
458            if self.outer_join:
459                join_operator = 'LEFT OUTER JOIN'
460            else:
461                join_operator = 'JOIN'
462          stmt = ("INSERT INTO %s (id, %s) SELECT %s.id, %s FROM %s"          stmt = ("INSERT INTO %s (id, %s) SELECT %s.id, %s FROM %s"
463                  " JOIN %s ON %s = %s;"                  " %s %s ON %s = %s;"
464                  % (self.tablename,                  % (self.tablename,
465                     ", ".join(internal_names),                     ", ".join(internal_names),
466                     self.left_table.tablename,                     self.left_table.tablename,
467                     ", ".join(internal_names),                     ", ".join(internal_names),
468                     self.left_table.tablename,                     self.left_table.tablename,
469                       join_operator,
470                     self.right_table.tablename,                     self.right_table.tablename,
471                     self.orig_to_internal[self.left_field],                     self.orig_to_internal[self.left_field],
472                     self.orig_to_internal[self.right_field]))                     self.orig_to_internal[self.right_field]))
473          self.db.execute(stmt)          self.db.execute(stmt)
474    
475        def Dependencies(self):
476            """Return a tuple with the two tables the join depends on."""
477            return self.dependencies
478    
479  class AutoTransientTable(table.OldTableInterfaceMixin):  
480    class AutoTransientTable(TitledObject, table.OldTableInterfaceMixin):
481    
482      """Table that copies data to a transient table on demand.      """Table that copies data to a transient table on demand.
483    
# Line 363  class AutoTransientTable(table.OldTableI Line 486  class AutoTransientTable(table.OldTableI
486      """      """
487    
488      def __init__(self, transient_db, table):      def __init__(self, transient_db, table):
489            TitledObject.__init__(self, table.Title())
490          self.transient_db = transient_db          self.transient_db = transient_db
491          self.table = table          self.table = table
492          self.t_table = None          self.t_table = None
# Line 392  class AutoTransientTable(table.OldTableI Line 516  class AutoTransientTable(table.OldTableI
516          else:          else:
517              return self.table.ReadRowAsDict(record)              return self.table.ReadRowAsDict(record)
518    
519        def ReadValue(self, row, col):
520            """Return the value of the specified row and column
521    
522            The col parameter may be the index of the column or its name.
523            """
524            if self.t_table is not None:
525                return self.t_table.ReadValue(row, col)
526            else:
527                return self.table.ReadValue(row, col)
528    
529      def copy_to_transient(self):      def copy_to_transient(self):
530          """Internal: Create a transient table and copy the data into it"""          """Internal: Create a transient table and copy the data into it"""
531          self.t_table = TransientTable(self.transient_db, self)          self.t_table = TransientTable(self.transient_db, self)
# Line 413  class AutoTransientTable(table.OldTableI Line 547  class AutoTransientTable(table.OldTableI
547          if self.t_table is None:          if self.t_table is None:
548              self.copy_to_transient()              self.copy_to_transient()
549          return self.t_table.UniqueValues(col)          return self.t_table.UniqueValues(col)
550    
551        def SimpleQuery(self, left, comparison, right):
552            if self.t_table is None:
553                self.copy_to_transient()
554            # Make sure to use the column object of the transient table. The
555            # left argument is always a column object so we can just ask the
556            # t_table for the right object.
557            if hasattr(right, "name"):
558                return self.t_table.SimpleQuery(self.t_table.Column(left.name),
559                                                comparison,
560                                                self.t_table.Column(right.name))
561            else:
562                return self.t_table.SimpleQuery(self.t_table.Column(left.name),
563                                                comparison, right)
564    
565        def Dependencies(self):
566            """Return a tuple containing the original table"""
567            return (self.table,)
568    

Legend:
Removed from v.841  
changed lines
  Added in v.1026

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26