Contact: fumanchu@aminus.org

Log in as guest/geniusql to create tickets

Changeset 27

Show
Ignore:
Timestamp:
03/10/07 14:35:13
Author:
fumanchu
Message:

New Database.pks_must_be_indexed attribute. Also reworked SQLite temp tables to re-use the existing Table object rather than replacing it.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/geniusql/objects.py

    r26 r27  
    1313    """Bijective dict. Each key maps to only one value (and vice-versa).""" 
    1414     
    15     def key_for(self, value): 
     15    def key_for(self, obj): 
    1616        """For the given value, return its corresponding key.""" 
    1717        for key, val in self.iteritems(): 
    18             if val is value
     18            if val is obj
    1919                return key 
    20         raise ValueError("The given value could not be found: %r" % value
     20        raise ValueError("The given object could not be found: %r" % obj
    2121     
    2222    def alias(self, oldname, newname): 
     
    757757    at a time should inspect this value to determine whether they 
    758758    need a separate Database instance per Schema instance. 
     759    """ 
     760     
     761    pks_must_be_indexed = True 
     762    pks_must_be_indexed__doc = """If True, the underlying database 
     763    implements primary keys by creating an index. Geniusql allows 
     764    you to define pk's without indexes, but not all databases do. 
    759765    """ 
    760766     
  • trunk/geniusql/providers/sqlite.py

    r26 r27  
    315315    """ 
    316316     
    317     def _temp_copy(self): 
    318         # Make a temporary table. Callers then modify it before binding. 
    319         schema = self.schema 
    320         temptable = self.copy() 
    321         tempkey = "temp_" + schema.key_for(self) 
    322         temptable.name = schema.table_name(tempkey) 
    323         temptable.qname = schema.db.quote(temptable.name) 
    324         # Update index names. 
    325         for key, i in temptable.indices.iteritems(): 
    326             i.name = schema.table_name("i" + temptable.name + i.colname) 
    327             i.qname = schema.db.quote(i.name) 
    328             i.tablename = temptable.name 
    329         return tempkey, temptable 
    330      
    331     def _copy_from_temp(self, temptable, tempkey): 
    332         """Copy data from a temp table to a new table for self.""" 
    333         schema = self.schema 
    334          
    335         # Drop the old table and create the new, final table. 
    336         newtable = temptable.copy() 
    337         newtable.name = self.name 
    338         newtable.qname = self.qname 
    339         # Update index names. 
    340         for key, i in newtable.indices.iteritems(): 
    341             i.name = schema.table_name("i" + newtable.name + i.colname) 
    342             i.qname = schema.db.quote(i.name) 
    343             i.tablename = newtable.name 
    344         schema[schema.key_for(self)] = newtable 
     317    def _start_temp(self): 
     318        """Convert self into a temporary table. Not thread-safe.""" 
     319        self.origname = self.name 
     320        self.origqname = self.qname 
     321        self.name = "temp_" + self.name 
     322        self.qname = self.schema.db.quote(self.name) 
     323     
     324    def _finish_temp(self, selfields=None): 
     325        """Convert self from a temporary table. Not thread-safe.""" 
     326        # CREATE the temporary TABLE. 
     327        self.schema._create_table(self, skip_indices=True) 
     328         
     329        tempqname = self.qname 
     330         
     331        # Copy data from the original table to the temp table. 
     332        if selfields is None: 
     333            selfields = [c.qname for c in self.itervalues()] 
     334        self.schema.db.execute_ddl("INSERT INTO %s SELECT %s FROM %s;" % 
     335                                   (tempqname, ", ".join(selfields), 
     336                                    self.origqname)) 
     337         
     338        # DROP the original TABLE. 
     339        self.schema.db.execute_ddl('DROP TABLE %s;' % self.origqname) 
     340         
     341        # CREATE the new TABLE. Note we do not skip indices here; 
     342        # SQLite dropped the old ones when we dropped the original table. 
     343        self.name = self.origname 
     344        self.qname = self.origqname 
     345        self.schema._create_table(self) 
    345346         
    346347        # Copy data from the temp table to the final table. 
    347348        # For some odd reason, using "SELECT *" 
    348349        # mixes up the fields (during rename, at least). 
    349         selfields = ", ".join([c.qname for c in temptable.values()]) 
    350         schema.db.execute("INSERT INTO %s (%s) SELECT %s FROM %s;" % 
    351                           (newtable.qname, selfields, selfields, 
    352                            temptable.qname)) 
    353          
    354         # Drop the intermediate table
    355         del schema[tempkey] 
     350        selfields = ", ".join([c.qname for c in self.values()]) 
     351        self.schema.db.execute("INSERT INTO %s (%s) SELECT %s FROM %s;" 
     352                               % (self.qname, selfields, selfields, 
     353                                  tempqname)) 
     354         
     355        # DROP the temporary TABLE
     356        self.schema.db.execute_ddl('DROP TABLE %s;' % tempqname) 
    356357     
    357358    if not _add_column_support: 
     
    364365                del self[key] 
    365366             
    366             if column.autoincrement: 
    367                 # This may or may not be a no-op, depending on the DB. 
    368                 self.schema.create_sequence(self, column) 
    369              
    370             # Make a temporary copy. 
    371             tempkey, temptable = self._temp_copy() 
    372             # Add the new column to the copy. 
    373             dict.__setitem__(temptable, key, column) 
    374             # Bind the temp table to the DB (this will call CREATE TABLE). 
    375             self.schema[tempkey] = temptable 
    376              
    377             # Copy data from the old table to the temp table. 
     367            self._start_temp() 
     368            dict.__setitem__(self, key, column) 
     369             
    378370            selfields = [] 
    379             for k, c in temptable.iteritems(): 
     371            for k, c in self.iteritems(): 
    380372                qname = c.qname 
    381373                if k == key: 
     
    383375                    qname = "NULL AS %s" % qname 
    384376                selfields.append(qname) 
    385             self.schema.db.execute_ddl("INSERT INTO %s SELECT %s FROM %s;" % 
    386                                        (temptable.qname, ", ".join(selfields), 
    387                                         self.qname)) 
    388              
    389             # Copy data from the temp table to a new table for self. 
    390             self._copy_from_temp(temptable, tempkey) 
     377            self._finish_temp(selfields) 
    391378     
    392379    def __delitem__(self, key): 
     
    398385            return 
    399386         
     387        self._start_temp() 
     388        dict.__delitem__(self, key) 
     389        self._finish_temp() 
     390         
    400391        column = self[key] 
    401          
    402         # Make a temporary copy. 
    403         tempkey, temptable = self._temp_copy() 
    404         # Drop the column from the copy. 
    405         dict.__delitem__(temptable, key) 
    406         # Bind the temp table to the DB (this will call CREATE TABLE). 
    407         self.schema[tempkey] = temptable 
    408          
    409         # Copy data from the old table to the temp table. 
    410         selfields = [c.qname for c in temptable.itervalues()] 
    411         self.schema.db.execute_ddl("INSERT INTO %s SELECT %s FROM %s;" % 
    412                                    (temptable.qname, ", ".join(selfields), 
    413                                     self.qname)) 
    414          
    415         self._copy_from_temp(temptable, tempkey) 
    416          
    417392        if column.autoincrement: 
    418393            # This may or may not be a no-op, depending on the DB. 
     
    426401         
    427402        if oldname != newname: 
     403            self._start_temp() 
     404             
    428405            dict.__delitem__(self, oldkey) 
    429406            dict.__setitem__(self, newkey, oldcol) 
     
    431408            oldcol.qname = self.schema.db.quote(newname) 
    432409             
    433             # Make a temporary copy. 
    434             tempkey, temptable = self._temp_copy() 
    435             # Bind the temp table to the DB (this will call CREATE TABLE). 
    436             self.schema[tempkey] = temptable 
    437              
    438             # Copy data from the old table to the temp table. 
    439410            selfields = [] 
    440             for k, c in temptable.iteritems(): 
     411            for k, c in self.iteritems(): 
    441412                qname = c.qname 
    442413                if k == newkey: 
    443414                    qname = "%s AS %s" % (self.schema.db.quote(oldname), qname) 
    444415                selfields.append(qname) 
    445             self.schema.db.execute_ddl("INSERT INTO %s SELECT %s FROM %s;" % 
    446                                        (temptable.qname, ", ".join(selfields), 
    447                                         self.qname)) 
    448              
    449             self._copy_from_temp(temptable, tempkey) 
     416            self._finish_temp(selfields) 
    450417     
    451418    def _grab_new_ids(self, idkeys, conn): 
     
    457424     
    458425    def add_primary(self): 
    459         """Set the PRIMARY KEY for this Table, using its Column.key values.""" 
     426        """Assert the PRIMARY KEY for this Table, using its Column.key values.""" 
    460427        pk = [column.qname for column in self.itervalues() if column.key] 
    461428        if pk: 
    462             # Make a temporary copy. 
    463             tempkey, temptable = self._temp_copy() 
    464             # Bind the temp table to the DB. 
    465             self.schema[tempkey] = temptable 
    466             # Copy data from the old table to the temp table. 
    467             selfields = [c.qname for c in temptable.itervalues()] 
    468             self.schema.db.execute_ddl("INSERT INTO %s SELECT %s FROM %s;" % 
    469                                        (temptable.qname, ", ".join(selfields), 
    470                                         self.qname)) 
    471             # Create the new table and copy data from the temp table to it. 
    472             self._copy_from_temp(temptable, tempkey) 
     429            self._start_temp() 
     430            self._finish_temp() 
    473431     
    474432    def drop_primary(self): 
    475433        """Remove any PRIMARY KEY for this Table.""" 
    476         # Make a temporary copy. 
    477         tempkey, temptable = self._temp_copy() 
    478         # Drop all .key's from the copy. 
    479         for col in temptable.itervalues(): 
     434        self._start_temp() 
     435        for col in self.itervalues(): 
    480436            col.key = False 
    481         # Bind the temp table to the DB. 
    482         self.schema[tempkey] = temptable 
    483          
    484         # Copy data from the old table to the temp table. 
    485         selfields = [c.qname for c in temptable.itervalues()] 
    486         self.schema.db.execute_ddl("INSERT INTO %s SELECT %s FROM %s;" % 
    487                                    (temptable.qname, ", ".join(selfields), 
    488                                     self.qname)) 
    489         # Create the new table and copy data from the temp table to it. 
    490         self._copy_from_temp(temptable, tempkey) 
     437        self._finish_temp() 
    491438 
    492439 
     
    708655        data, _ = self.db.fetch( 
    709656            "SELECT name, tbl_name, sql FROM sqlite_master " 
    710             "WHERE type = 'index';", conn) 
     657            "WHERE type = 'index' AND tbl_name = '%s';" % tablename, conn) 
    711658         
    712659        indices = [] 
     
    768715            del self[key] 
    769716         
     717        self._create_table(table) 
     718         
    770719        # Set table.created to True, which should "turn on" 
    771720        # any future ALTER TABLE statements. 
    772721        table.created = True 
    773722         
     723        dict.__setitem__(self, key, table) 
     724     
     725    def _create_table(self, table, skip_indices=False): 
    774726        fields = [] 
    775727        pk = [] 
     
    795747                            (table.qname, ", ".join(fields), pk)) 
    796748         
    797         for index in table.indices.itervalues(): 
    798             self.db.execute_ddl('CREATE INDEX %s ON %s (%s);' % 
    799                                 (index.qname, table.qname, 
    800                                  self.db.quote(index.colname))) 
    801          
    802749        if autoincr_col: 
    803750            self.create_sequence(table, autoincr_col) 
    804751         
    805         dict.__setitem__(self, key, table) 
     752        if not skip_indices: 
     753            for index in table.indices.itervalues(): 
     754                self.db.execute_ddl('CREATE INDEX %s ON %s (%s);' % 
     755                                    (index.qname, table.qname, 
     756                                     self.db.quote(index.colname))) 
    806757     
    807758    def _rename(self, oldtable, newtable): 
     
    836787    schemaclass = SQLiteSchema 
    837788    multischema = False 
     789     
     790    pks_must_be_indexed = False 
    838791     
    839792    def __init__(self, **kwargs): 
  • trunk/geniusql/test/zoo_fixture.py

    r26 r27  
    4141        Animal = schema.table('Animal') 
    4242        Animal['ID'] = schema.column(int, autoincrement=True, key=True) 
    43         Animal.add_index('ID') 
     43##        Animal.add_index('ID') 
    4444        Animal['ZooID'] = schema.column(int) 
    4545        Animal['Name'] = schema.column(hints={'bytes': 100}) 
     
    606606    def test_primary_key_support(self): 
    607607        # Drop and re-add the PK on, oh, how about the Animal table? 
    608         schema['Animal'].drop_primary() 
    609         schema['Animal'].add_primary() 
     608        Animal = schema['Animal'] 
     609        Animal.drop_primary() 
     610        Animal.add_primary() 
     611         
     612        Animal = schema.discover('Animal') 
     613        if schema.db.pks_must_be_indexed: 
     614            self.assertEqual(len(Animal.indices), 2) 
     615        else: 
     616            # Since we did not add an index on Animal.ID... 
     617            self.assertEqual(len(Animal.indices), 1) 
    610618     
    611619    def test_insert_into(self):